<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Zhenguo Zhang&#39;s Blog</title>
    <link>https://fortune9.netlify.app/</link>
    <description>Recent content on Zhenguo Zhang&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 30 Aug 2026 00:00:00 +0000</lastBuildDate>
    
        <atom:link href="https://fortune9.netlify.app/index.xml" rel="self" type="application/rss+xml" />
    
    
    
        <item>
        <title>[R] Understanding position_dodge() and position_dodge2() in ggplot2</title>
        <link>https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/</link>
        <pubDate>Sun, 30 Aug 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/ -


&lt;p&gt;In &lt;code&gt;ggplot2&lt;/code&gt;, when displaying grouped data along categorical axes (such as grouped bar charts, boxplots, or error bars), horizontal dodging is used to prevent elements from overlapping at each categorical position. &lt;code&gt;ggplot2&lt;/code&gt; provides two primary dodging position functions: &lt;code&gt;position_dodge()&lt;/code&gt; and &lt;code&gt;position_dodge2()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this post, we will explore:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;How the &lt;code&gt;width&lt;/code&gt; parameter in &lt;code&gt;position_dodge()&lt;/code&gt; interacts with the parent geom’s &lt;code&gt;width&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The core differences between &lt;code&gt;position_dodge2()&lt;/code&gt; and &lt;code&gt;position_dodge()&lt;/code&gt;, including why changing &lt;code&gt;width&lt;/code&gt; in &lt;code&gt;position_dodge2()&lt;/code&gt; often has no visual effect.&lt;/li&gt;
&lt;li&gt;How &lt;code&gt;preserve = &#34;single&#34;&lt;/code&gt; behaves when elements of a group variable are missing at a given x-axis level.&lt;/li&gt;
&lt;/ol&gt;
&lt;div id=&#34;definition-of-terms&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Definition of Terms&lt;/h3&gt;
&lt;p&gt;To keep our explanations clear and consistent throughout this post, we define two key concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;x-axis level&lt;/strong&gt;: The discrete value or category on the x-axis. In our examples using &lt;code&gt;mtcars&lt;/code&gt;, the x-axis level is &lt;code&gt;cyl&lt;/code&gt; (with levels &lt;code&gt;4&lt;/code&gt;, &lt;code&gt;6&lt;/code&gt;, and &lt;code&gt;8&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;group variable&lt;/strong&gt;: The variable that divides items into subgroups at each x-axis level, typically mapped to an aesthetic like &lt;code&gt;fill&lt;/code&gt; or &lt;code&gt;color&lt;/code&gt;. In our examples, the group variable is &lt;code&gt;gear&lt;/code&gt; (with levels &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;4&lt;/code&gt;, and &lt;code&gt;5&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dodged elements&lt;/strong&gt;: The individual visual marks (e.g. bars) corresponding to each level of the group variable at a specific x-axis level.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)
library(patchwork)&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;div id=&#34;the-width-parameter-in-position_dodge&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;1. The &lt;code&gt;width&lt;/code&gt; Parameter in &lt;code&gt;position_dodge()&lt;/code&gt;&lt;/h2&gt;
&lt;div id=&#34;what-does-width-control&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;What does &lt;code&gt;width&lt;/code&gt; control?&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;position_dodge(width = ...)&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt; specifies the &lt;strong&gt;total dodging span&lt;/strong&gt; (interval) on the x-axis into which all dodged elements for a given x-axis level are arranged side-by-side.&lt;/p&gt;
&lt;p&gt;By default, if you do not explicitly supply &lt;code&gt;width&lt;/code&gt; to &lt;code&gt;position_dodge()&lt;/code&gt;, it &lt;strong&gt;inherits its value from the parent geom&lt;/strong&gt; (e.g., &lt;code&gt;geom_bar()&lt;/code&gt; defaults to &lt;code&gt;width = 0.9&lt;/code&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;interaction-between-geom_barwidth-and-position_dodgewidth&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Interaction between &lt;code&gt;geom_bar(width)&lt;/code&gt; and &lt;code&gt;position_dodge(width)&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;geom_bar(width = ...)&lt;/code&gt;&lt;/strong&gt;: Sets the total physical width allocated to the bars at each x-axis level. The individual bar width is derived by dividing this value by the number of elements at that x-axis level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;position_dodge(width = ...)&lt;/code&gt;&lt;/strong&gt;: Sets the total span on the x-axis across which the center positions of the dodged elements are calculated and spaced.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both parameters use x-axis coordinate units (where the distance between adjacent discrete x-axis levels is &lt;code&gt;1.0&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Let’s examine how varying both parameters affects the layout using the &lt;code&gt;mtcars&lt;/code&gt; dataset (plotting mean &lt;code&gt;mpg&lt;/code&gt; across x-axis level &lt;code&gt;cyl&lt;/code&gt;, with group variable &lt;code&gt;gear&lt;/code&gt; mapped to &lt;code&gt;fill&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Base plot configuration
base_p &amp;lt;- ggplot(mtcars, aes(x = factor(cyl), y = mpg, fill = factor(gear))) +
  labs(x = &amp;quot;Cylinders (x-axis level)&amp;quot;, y = &amp;quot;Mean MPG&amp;quot;, fill = &amp;quot;Gear (group variable)&amp;quot;) +
  theme_minimal(base_size = 11)

# Case 1: Default matching widths (0.9 / 0.9)
# Bars touch neatly within each x-axis level
p1 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    width = 0.9, 
    position = position_dodge(width = 0.9)
  ) +
  ggtitle(&amp;quot;1. Matching widths (0.9 / 0.9)&amp;quot;, subtitle = &amp;quot;Bars touch neatly within each x-axis level&amp;quot;)

# Case 2: geom_bar width (0.5) &amp;lt; dodge width (0.9)
# Dodging span is 0.9, but bars are narrower -&amp;gt; gaps appear between bars
p2 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    width = 0.5, 
    position = position_dodge(width = 0.9)
  ) +
  ggtitle(&amp;quot;2. geom_bar(0.5) &amp;lt; dodge(0.9)&amp;quot;, subtitle = &amp;quot;Gaps created between dodged elements&amp;quot;)

# Case 3: geom_bar width (0.9) &amp;gt; dodge width (0.5)
# Dodging span is narrower than bar widths -&amp;gt; bars overlap
p3 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    width = 0.9, 
    position = position_dodge(width = 0.5)
  ) +
  ggtitle(&amp;quot;3. geom_bar(0.9) &amp;gt; dodge(0.5)&amp;quot;, subtitle = &amp;quot;Dodged elements overlap&amp;quot;)

# Case 4: Both changed to 0.6
# Group as a whole is narrower, bars touch, larger separation between x-axis levels
p4 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    width = 0.6, 
    position = position_dodge(width = 0.6)
  ) +
  ggtitle(&amp;quot;4. Both set to 0.6&amp;quot;, subtitle = &amp;quot;Compact cluster, wider space between x-axis levels&amp;quot;)

(p1 | p2) / (p3 | p4)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/index_files/figure-html/section-1-dodge-width-1.png&#34; alt=&#34;&#34; width=&#34;1800&#34; style=&#34;display: block; margin: auto;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;As demonstrated:
- Increasing &lt;code&gt;width&lt;/code&gt; in &lt;code&gt;geom_bar()&lt;/code&gt; increases the individual width of dodged elements at that x-axis level.
- Increasing &lt;code&gt;width&lt;/code&gt; in &lt;code&gt;position_dodge()&lt;/code&gt; increases the total dodging span across which elements are spread.
- When both values are equal, the dodged elements within each x-axis level touch each other without overlapping or leaving gaps.&lt;/p&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;differences-between-position_dodge2-and-position_dodge&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;2. Differences Between &lt;code&gt;position_dodge2()&lt;/code&gt; and &lt;code&gt;position_dodge()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;position_dodge()&lt;/code&gt; was originally designed for simple geoms with fixed-width positions (like &lt;code&gt;geom_bar&lt;/code&gt; and &lt;code&gt;geom_col&lt;/code&gt;), &lt;code&gt;position_dodge2()&lt;/code&gt; was introduced to handle geoms with variable widths or explicit intervals (&lt;code&gt;xmin&lt;/code&gt; to &lt;code&gt;xmax&lt;/code&gt;), such as &lt;code&gt;geom_boxplot()&lt;/code&gt;, &lt;code&gt;geom_rect()&lt;/code&gt;, and &lt;code&gt;geom_linerange()&lt;/code&gt;.&lt;/p&gt;
&lt;div id=&#34;key-differences&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Key Differences:&lt;/h3&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;&lt;strong&gt;Boundary-Based Packing vs. Fixed Slot Offsets&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;position_dodge()&lt;/code&gt; divides the total dodge width at an x-axis level into fixed slots and places each level of the group variable into its designated slot.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;position_dodge2()&lt;/code&gt; packs elements (for different levels of the group variable) sequentially side-by-side using the bounding boxes of the geoms.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &lt;code&gt;width&lt;/code&gt; Parameter Has No Visual Effect in &lt;code&gt;position_dodge2()&lt;/code&gt; with Bars&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Because &lt;code&gt;position_dodge2()&lt;/code&gt; arranges bars based on their pre-computed bounding intervals rather than calculating center slot offsets from a dodge span, changing &lt;code&gt;width&lt;/code&gt; in &lt;code&gt;position_dodge2()&lt;/code&gt; produces no change in the plot output when bar widths are already defined by the parent geom.&lt;/li&gt;
&lt;li&gt;Bar widths must be controlled directly via &lt;code&gt;geom_bar(width = ...)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;reverse&lt;/code&gt; Support&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;padding&lt;/code&gt;: Adds proportional space between dodged elements at the same x-axis level without requiring manual mismatch of geom and dodge widths.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reverse = TRUE&lt;/code&gt;: Reverses the left-to-right plotting order of the group variable without needing to alter factor levels.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div id=&#34;demo-width-has-no-effect-in-position_dodge2&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Demo: &lt;code&gt;width&lt;/code&gt; Has No Effect in &lt;code&gt;position_dodge2()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Below, we compare &lt;code&gt;position_dodge2(width = 0.3)&lt;/code&gt; against &lt;code&gt;position_dodge2(width = 0.9)&lt;/code&gt;. Notice that the two plots are completely identical:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Changing width in position_dodge2 produces identical output
p_d2_w03 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    position = position_dodge2(width = 0.3)
  ) +
  ggtitle(&amp;quot;position_dodge2(width = 0.3)&amp;quot;, subtitle = &amp;quot;Width parameter has no visual effect&amp;quot;)

p_d2_w09 &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    position = position_dodge2(width = 0.9)
  ) +
  ggtitle(&amp;quot;position_dodge2(width = 0.9)&amp;quot;, subtitle = &amp;quot;Identical layout to width = 0.3&amp;quot;)

p_d2_w03 | p_d2_w09&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/index_files/figure-html/section-2-dodge2-no-effect-1.png&#34; alt=&#34;&#34; width=&#34;2000&#34; style=&#34;display: block; margin: auto;&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;creating-spacing-with-padding-and-reversing-order-with-reverse&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Creating Spacing with &lt;code&gt;padding&lt;/code&gt; and Reversing Order with &lt;code&gt;reverse&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Instead of altering &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;position_dodge2()&lt;/code&gt; provides the &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;reverse&lt;/code&gt; parameters:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Using padding and reverse in position_dodge2
p_dodge2_demo &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    position = position_dodge2(padding = 0.2, reverse = TRUE)
  ) +
  labs(
    title = &amp;quot;position_dodge2(padding = 0.2, reverse = TRUE)&amp;quot;,
    subtitle = &amp;quot;Built-in bar padding and reversed order of group variable&amp;quot;
  )

p_dodge2_demo&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/index_files/figure-html/section-2-dodge2-padding-reverse-1.png&#34; alt=&#34;&#34; width=&#34;1800&#34; style=&#34;display: block; margin: auto;&#34; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;handling-missing-elements-with-preserve-single&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;3. Handling Missing Elements with &lt;code&gt;preserve = &#34;single&#34;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;A significant functional difference between &lt;code&gt;position_dodge()&lt;/code&gt; and &lt;code&gt;position_dodge2()&lt;/code&gt; occurs when certain levels of the group variable are absent at a specific x-axis level.&lt;/p&gt;
&lt;p&gt;Let’s inspect the count of observations across &lt;code&gt;cyl&lt;/code&gt; (x-axis level) and &lt;code&gt;gear&lt;/code&gt; (group variable) in &lt;code&gt;mtcars&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;table(mtcars$cyl, mtcars$gear)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    
##      3  4  5
##   4  1  8  2
##   6  2  4  1
##   8 12  0  2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the x-axis level &lt;strong&gt;&lt;code&gt;cyl = 8&lt;/code&gt;&lt;/strong&gt;, the group variable &lt;code&gt;gear&lt;/code&gt; contains observations for gears &lt;code&gt;3&lt;/code&gt; (12 cars) and &lt;code&gt;5&lt;/code&gt; (2 cars), but &lt;strong&gt;&lt;code&gt;gear = 4&lt;/code&gt; is completely missing (0 cars)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When we set &lt;code&gt;preserve = &#34;single&#34;&lt;/code&gt; to ensure that all individual bar widths remain uniform across all x-axis levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;position_dodge(preserve = &#34;single&#34;)&lt;/code&gt;&lt;/strong&gt;: Retains fixed slot assignments. Because &lt;code&gt;gear = 4&lt;/code&gt; is missing at &lt;code&gt;cyl = 8&lt;/code&gt;, it leaves a &lt;strong&gt;blank gap&lt;/strong&gt; in the middle where &lt;code&gt;gear = 4&lt;/code&gt; would normally reside.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;position_dodge2(preserve = &#34;single&#34;)&lt;/code&gt;&lt;/strong&gt;: Preserves individual bar width while &lt;strong&gt;re-centering the remaining dodged elements&lt;/strong&gt;, packing them side-by-side without leaving an empty gap.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# position_dodge with preserve = &amp;quot;single&amp;quot;
p_dodge_single &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    position = position_dodge(preserve = &amp;quot;single&amp;quot;)
  ) +
  labs(
    title = &amp;quot;position_dodge(preserve = &amp;#39;single&amp;#39;)&amp;quot;,
    subtitle = &amp;quot;Leaves an empty gap for missing gear = 4 at cyl = 8&amp;quot;
  )

# position_dodge2 with preserve = &amp;quot;single&amp;quot;
p_dodge2_single &amp;lt;- base_p +
  geom_bar(
    stat = &amp;quot;summary&amp;quot;, 
    fun = &amp;quot;mean&amp;quot;, 
    position = position_dodge2(preserve = &amp;quot;single&amp;quot;)
  ) +
  labs(
    title = &amp;quot;position_dodge2(preserve = &amp;#39;single&amp;#39;)&amp;quot;,
    subtitle = &amp;quot;Re-centers remaining elements (no gap at cyl = 8)&amp;quot;
  )

# Side-by-side comparison
p_dodge_single | p_dodge2_single&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/index_files/figure-html/section-3-preserve-single-1.png&#34; alt=&#34;&#34; width=&#34;2000&#34; style=&#34;display: block; margin: auto;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Note that if one sets preserve = “total”, then you would not see any difference, because the bars are re-stretched
to ensure the bars from each categorical level occupy all the width assigned to that level.&lt;/p&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;div id=&#34;summary&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col width=&#34;33%&#34; /&gt;
&lt;col width=&#34;33%&#34; /&gt;
&lt;col width=&#34;33%&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;Feature&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;&lt;code&gt;position_dodge()&lt;/code&gt;&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;&lt;code&gt;position_dodge2()&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Primary Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Simple 1D fixed-width geoms (&lt;code&gt;geom_bar&lt;/code&gt;, &lt;code&gt;geom_col&lt;/code&gt;)&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Interval &amp;amp; variable-width geoms (&lt;code&gt;geom_boxplot&lt;/code&gt;, &lt;code&gt;geom_rect&lt;/code&gt;, &lt;code&gt;geom_linerange&lt;/code&gt;) and bars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Role of &lt;code&gt;width&lt;/code&gt; Parameter&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Sets total dodging span across each x-axis level&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Has no visual effect on bars with predefined geom width&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Missing Elements (&lt;code&gt;preserve=&#34;single&#34;&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Leaves empty slot / gap at the missing level&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Re-centers remaining dodged elements together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Padding Between Elements&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Manual (&lt;code&gt;geom_bar(width) &amp;lt; position_dodge(width)&lt;/code&gt;)&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Built-in via &lt;code&gt;padding = ...&lt;/code&gt; argument&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Reverse Plotting Order&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Requires re-leveling the group variable&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Built-in via &lt;code&gt;reverse = TRUE&lt;/code&gt; argument&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
- https://fortune9.netlify.app/2026/08/30/r-understanding-position-dodge-and-position-dodge2-in-ggplot2/ - </description>
        </item>
    
    
    
        <item>
        <title>[Linux] Handling grep Exit Codes in Scripts with set -e and set -o pipefail</title>
        <link>https://fortune9.netlify.app/2026/08/15/linux-handling-grep-exit-codes-in-scripts-with-set-e-and-set-o-pipefail/</link>
        <pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/08/15/linux-handling-grep-exit-codes-in-scripts-with-set-e-and-set-o-pipefail/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/08/15/linux-handling-grep-exit-codes-in-scripts-with-set-e-and-set-o-pipefail/ -&lt;p&gt;When writing robust Bash scripts, it is common best practice to enable strict mode options:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;set -e          &lt;span style=&#34;color:#75715e&#34;&gt;# Exit immediately if a command exits with a non-zero status&lt;/span&gt;
set -o pipefail &lt;span style=&#34;color:#75715e&#34;&gt;# Pipeline exit status is the value of the last (rightmost) command to exit with non-zero status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;While these settings prevent errors from silently propagating, they frequently cause unexpected script failures when working with &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this post, we will explore why &lt;code&gt;grep&lt;/code&gt; exit codes break strict shell scripts, demonstrate the problem with clear examples, and detail four practical solutions to address it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;understanding-grep-exit-codes&#34;&gt;Understanding &lt;code&gt;grep&lt;/code&gt; Exit Codes&lt;/h2&gt;
&lt;p&gt;Unlike many Unix tools where any non-zero exit code indicates a fatal error, &lt;code&gt;grep&lt;/code&gt; uses exit codes to communicate search outcomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Exit Code &lt;code&gt;0&lt;/code&gt;&lt;/strong&gt;: One or more matching lines were found.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exit Code &lt;code&gt;1&lt;/code&gt;&lt;/strong&gt;: &lt;strong&gt;No&lt;/strong&gt; matching lines were found (a valid, expected outcome in many workflows).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exit Code &lt;code&gt;2&lt;/code&gt; (or &amp;gt;1)&lt;/strong&gt;: An error occurred (e.g., syntax error in regex, missing file, permission denied).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because shell safety directives like &lt;code&gt;set -e&lt;/code&gt; treats &lt;em&gt;any&lt;/em&gt; non-zero status as a command failure, &lt;code&gt;grep&lt;/code&gt; returning &lt;code&gt;1&lt;/code&gt; when no matches are found will cause the shell script to abort immediately. Similarly, &lt;code&gt;set -o pipefail&lt;/code&gt; causes pipelines containing &lt;code&gt;grep&lt;/code&gt; to fail if no lines match the query.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-problem-in-action&#34;&gt;The Problem in Action&lt;/h2&gt;
&lt;h3 id=&#34;example-1-direct-grep-under-set--e&#34;&gt;Example 1: Direct &lt;code&gt;grep&lt;/code&gt; under &lt;code&gt;set -e&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Consider a script designed to process error logs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;set -e

echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting log analysis...&amp;#34;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Try to find warnings in the log&lt;/span&gt;
grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt; server.log &amp;gt; warnings.txt

echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Analysis complete!&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;If &lt;code&gt;server.log&lt;/code&gt; contains no lines matching &lt;code&gt;&amp;quot;WARNING&amp;quot;&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt; finishes cleanly without printing anything and returns an exit code of &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Under &lt;code&gt;set -e&lt;/code&gt;, the shell interprets this &lt;code&gt;1&lt;/code&gt; as a fatal execution failure. The script exits instantly at the &lt;code&gt;grep&lt;/code&gt; line—&lt;code&gt;warnings.txt&lt;/code&gt; may be left empty, and &lt;code&gt;&amp;quot;Analysis complete!&amp;quot;&lt;/code&gt; is never printed!&lt;/p&gt;
&lt;h3 id=&#34;example-2-pipelines-under-set--o-pipefail&#34;&gt;Example 2: Pipelines under &lt;code&gt;set -o pipefail&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Now consider a pipeline processing input:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;set -euo pipefail

&lt;span style=&#34;color:#75715e&#34;&gt;# Count how many critical errors occurred&lt;/span&gt;
CRITICAL_COUNT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat app.log | grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRITICAL&amp;#34;&lt;/span&gt; | wc -l&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;

echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Critical errors count: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;CRITICAL_COUNT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Without &lt;code&gt;set -o pipefail&lt;/code&gt;, the pipeline exit status would be that of &lt;code&gt;wc -l&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt;), hiding the fact that &lt;code&gt;grep&lt;/code&gt; returned &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, with &lt;code&gt;set -o pipefail&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;&amp;rsquo;s exit code &lt;code&gt;1&lt;/code&gt; propagates as the failure status of the entire pipeline. As a result, &lt;code&gt;set -e&lt;/code&gt; terminates the script before &lt;code&gt;CRITICAL_COUNT&lt;/code&gt; is assigned.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;solutions&#34;&gt;Solutions&lt;/h2&gt;
&lt;p&gt;Here are four effective ways to safely handle &lt;code&gt;grep&lt;/code&gt; when &lt;code&gt;set -e&lt;/code&gt; or &lt;code&gt;set -o pipefail&lt;/code&gt; is active.&lt;/p&gt;
&lt;h3 id=&#34;solution-1-append--true-or--&#34;&gt;Solution 1: Append &lt;code&gt;|| true&lt;/code&gt; (or &lt;code&gt;|| :&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;The simplest and most common quick fix is using the OR operator (&lt;code&gt;||&lt;/code&gt;) followed by &lt;code&gt;true&lt;/code&gt; (or the shell built-in &lt;code&gt;:&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt; server.log &amp;gt; warnings.txt &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;how-it-works&#34;&gt;How it works&lt;/h4&gt;
&lt;p&gt;If &lt;code&gt;grep&lt;/code&gt; succeeds (exit code &lt;code&gt;0&lt;/code&gt;), the right side of &lt;code&gt;||&lt;/code&gt; is ignored. If &lt;code&gt;grep&lt;/code&gt; exits with code &lt;code&gt;1&lt;/code&gt; (or &lt;code&gt;2&lt;/code&gt;), the &lt;code&gt;|| true&lt;/code&gt; executes, returning exit code &lt;code&gt;0&lt;/code&gt; and allowing &lt;code&gt;set -e&lt;/code&gt; to continue.&lt;/p&gt;
&lt;h4 id=&#34;pros--cons&#34;&gt;Pros &amp;amp; Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Simple, concise, one-liner fix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt;: It masks &lt;strong&gt;all&lt;/strong&gt; non-zero exit codes, including code &lt;code&gt;2&lt;/code&gt; (e.g., missing file or invalid regex pattern).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;solution-2-test--or-use-conditional-statements&#34;&gt;Solution 2: Test &lt;code&gt;$?&lt;/code&gt; or Use Conditional Statements&lt;/h3&gt;
&lt;p&gt;If you want to distinguish between &amp;ldquo;no matches found&amp;rdquo; (code &lt;code&gt;1&lt;/code&gt;) and a real error (code &lt;code&gt;2&lt;/code&gt;), you can check the exit status explicitly.&lt;/p&gt;
&lt;h4 id=&#34;approach-a-direct-if-statement&#34;&gt;Approach A: Direct &lt;code&gt;if&lt;/code&gt; statement&lt;/h4&gt;
&lt;p&gt;In Bash, commands tested inside &lt;code&gt;if&lt;/code&gt; statements or &lt;code&gt;if grep ...&lt;/code&gt; conditions do &lt;strong&gt;not&lt;/strong&gt; trigger &lt;code&gt;set -e&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt; server.log &amp;gt; warnings.txt; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Warnings were found.&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No warnings found (or grep error).&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;approach-b-preserving-and-evaluating-&#34;&gt;Approach B: Preserving and evaluating &lt;code&gt;$?&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;To specifically handle exit code &lt;code&gt;1&lt;/code&gt; vs &lt;code&gt;2&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Capture exit status without triggering set -e&lt;/span&gt;
grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt; server.log &amp;gt; warnings.txt &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; status&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$?

&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;status&lt;span style=&#34;color:#66d9ef&#34;&gt;:-&lt;/span&gt;0&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt; in
    0&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
        echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Found matches.&amp;#34;&lt;/span&gt;
        ;;
    1&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
        echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No matches found. Continuing execution...&amp;#34;&lt;/span&gt;
        ;;
    *&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
        echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;An error occurred with grep (exit code &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;status&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;). Exiting...&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
        exit &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;status&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
        ;;
&lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;pros--cons-1&#34;&gt;Pros &amp;amp; Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Precise error handling; preserves actual error detection for code &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Slightly more verbose.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;solution-3-use-awk-for-filtering&#34;&gt;Solution 3: Use &lt;code&gt;awk&lt;/code&gt; for Filtering&lt;/h3&gt;
&lt;p&gt;If you are filtering text in a pipeline under &lt;code&gt;set -o pipefail&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt; can serve as a robust drop-in replacement for &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt; returns exit code &lt;code&gt;0&lt;/code&gt; when matching zero lines (unless an unhandled script/syntax error occurs inside &lt;code&gt;awk&lt;/code&gt; itself).&lt;/p&gt;
&lt;h4 id=&#34;replacing-grep-with-awk&#34;&gt;Replacing &lt;code&gt;grep&lt;/code&gt; with &lt;code&gt;awk&lt;/code&gt;:&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Instead of: grep &amp;#34;PATTERN&amp;#34; input.txt&lt;/span&gt;
awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/PATTERN/&amp;#39;&lt;/span&gt; input.txt &amp;gt; output.txt

&lt;span style=&#34;color:#75715e&#34;&gt;# Instead of: cat log.txt | grep &amp;#34;CRITICAL&amp;#34; | wc -l&lt;/span&gt;
CRITICAL_COUNT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/CRITICAL/&amp;#39;&lt;/span&gt; log.txt | wc -l&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can even perform the counting directly in &lt;code&gt;awk&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;CRITICAL_COUNT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/CRITICAL/ {count++} END {print count+0}&amp;#39;&lt;/span&gt; log.txt&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;pros--cons-2&#34;&gt;Pros &amp;amp; Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Eliminates exit code &lt;code&gt;1&lt;/code&gt; issues entirely in pipelines while still returning non-zero on syntax or file access errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Syntax is slightly longer than standard &lt;code&gt;grep&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;solution-4-temporary-toggling-of-set-e-or-set-o-pipefail&#34;&gt;Solution 4: Temporary Toggling of &lt;code&gt;set +e&lt;/code&gt; or &lt;code&gt;set +o pipefail&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you have a block of commands where &lt;code&gt;grep&lt;/code&gt; exit codes should not terminate the script, you can temporarily disable strict checking and re-enable it afterwards.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Disable strict flags temporarily&lt;/span&gt;
set +e
set +o pipefail

grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt; server.log &amp;gt; warnings.txt
GREP_EXIT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$?

&lt;span style=&#34;color:#75715e&#34;&gt;# Re-enable strict flags&lt;/span&gt;
set -e
set -o pipefail

&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GREP_EXIT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -eq &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Warnings found.&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GREP_EXIT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -eq &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No warnings found.&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Grep failed with error code &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GREP_EXIT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
    exit &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GREP_EXIT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;pros--cons-3&#34;&gt;Pros &amp;amp; Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Gives explicit control over scope and allows inspecting exit codes without modifying the pipeline syntax.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Requires remembering to restore &lt;code&gt;set -e&lt;/code&gt; / &lt;code&gt;set -o pipefail&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;summary-comparison&#34;&gt;Summary Comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Method&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Syntax&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;Handles Code 1 (No Match)&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;Catches Code 2 (Errors)?&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Best Used For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;&lt;code&gt;|| true&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;grep ... || true&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;❌ Masked as 0&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Quick scripts, optional filters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Conditional (&lt;code&gt;if&lt;/code&gt; / &lt;code&gt;$?&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;grep ... || status=$?&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;✅ Yes&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Safe scripts needing exact error checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;&lt;code&gt;awk&lt;/code&gt; Filtering&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;awk &#39;/pattern/&#39; file&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;✅ Yes&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Pipelines with &lt;code&gt;set -o pipefail&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Flag Toggling&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;set +e ... set -e&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;Yes&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;✅ Yes&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Complex blocks or multi-command sections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Understanding how &lt;code&gt;grep&lt;/code&gt; exit codes interact with shell strict mode (&lt;code&gt;set -e&lt;/code&gt; and &lt;code&gt;set -o pipefail&lt;/code&gt;) will save you hours of debugging mysterious script exits (&lt;strong&gt;The main reason I write this post&lt;/strong&gt;). Depending on your security and logging needs, using &lt;code&gt;|| true&lt;/code&gt; works well for quick tasks, while &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;$?&lt;/code&gt; status checking or &lt;code&gt;awk&lt;/code&gt; pipeline filtering offers the most robust solution for production scripts.&lt;/p&gt;
- https://fortune9.netlify.app/2026/08/15/linux-handling-grep-exit-codes-in-scripts-with-set-e-and-set-o-pipefail/ - </description>
        </item>
    
    
    
        <item>
        <title>[R] Environment Variables in R Shiny-Server Container: Problem and Solutions</title>
        <link>https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/</link>
        <pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ -&lt;p&gt;When dockerizing an R Shiny application hosted via &lt;strong&gt;Shiny Server&lt;/strong&gt; (built from shiny server docker image &lt;a href=&#34;https://hub.docker.com/r/rocker/shiny)&#34;&gt;https://hub.docker.com/r/rocker/shiny)&lt;/a&gt;, a common issue developers face is that environment variables set via &lt;code&gt;ENV&lt;/code&gt; instructions in the &lt;code&gt;Dockerfile&lt;/code&gt; (or passed at runtime via &lt;code&gt;docker run -e&lt;/code&gt;) are completely missing inside the R Shiny app.&lt;/p&gt;
&lt;p&gt;For example, if you set the following environment variable in your &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt; SHOULD_IN_SHINY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;From dockerfile&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;When your app launches and runs &lt;code&gt;Sys.getenv(&amp;quot;SHOULD_IN_SHINY&amp;quot;)&lt;/code&gt;, it returns an empty string &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; instead of &lt;code&gt;&amp;quot;From dockerfile&amp;quot;&lt;/code&gt;.
This is not a problem with Docker itself, but rather a consequence of how &lt;strong&gt;Shiny Server&lt;/strong&gt; spawns R worker processes.&lt;/p&gt;
&lt;p&gt;In this post, we will look into the root cause behind this behavior in Shiny Server and demonstrate the two recommended solutions to correctly expose environment variables to your R Shiny workers.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-root-cause-how-shiny-server-spawns-r-processes&#34;&gt;The Root Cause: How Shiny Server Spawns R Processes&lt;/h2&gt;
&lt;p&gt;The reason environment variables do not carry over to your R session lies in how Shiny Server executes R worker processes inside the container.&lt;/p&gt;
&lt;p&gt;Shiny Server runs as a system service (typically as &lt;code&gt;root&lt;/code&gt; or &lt;code&gt;shiny&lt;/code&gt;). When launching an app instance, it re-executes R as the &lt;code&gt;shiny&lt;/code&gt; unprivileged user using &lt;code&gt;su&lt;/code&gt;. Specifically, the execution call combines two mutually exclusive flags:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;su shiny --login --preserve-environment -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;... R ...&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break down what these flags request from &lt;code&gt;su&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--login&lt;/code&gt; (&lt;code&gt;-l&lt;/code&gt;)&lt;/strong&gt;: Starts a &lt;strong&gt;login shell&lt;/strong&gt;. This intentionally &lt;strong&gt;resets&lt;/strong&gt; the environment to a minimal whitelist (&lt;code&gt;HOME&lt;/code&gt;, &lt;code&gt;PATH&lt;/code&gt;, &lt;code&gt;USER&lt;/code&gt;, &lt;code&gt;TERM&lt;/code&gt;, etc.) and sources system startup profiles (&lt;code&gt;/etc/profile&lt;/code&gt;, &lt;code&gt;~/.profile&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--preserve-environment&lt;/code&gt; (&lt;code&gt;-p&lt;/code&gt;)&lt;/strong&gt;: Explicitly asks &lt;code&gt;su&lt;/code&gt; to &lt;strong&gt;keep&lt;/strong&gt; the current environment inherited from the caller (which includes Docker&amp;rsquo;s &lt;code&gt;ENV&lt;/code&gt; variables).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because Linux&amp;rsquo;s &lt;code&gt;su&lt;/code&gt; utility cannot honor both conflicting behaviors, it chooses &lt;code&gt;--login&lt;/code&gt; and prints a warning:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;su: ignoring --preserve-environment, it&amp;#39;s mutually exclusive with --login
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This warning is emitted by Shiny Server&amp;rsquo;s underlying process execution call, not by the R app itself. Attempting to suppress or patch it requires modifying and rebuilding Shiny Server C++ code, which is rarely practical.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the &lt;strong&gt;login shell wins&lt;/strong&gt;, all custom environment variables passed to the container via &lt;code&gt;ENV&lt;/code&gt; or &lt;code&gt;docker run&lt;/code&gt; are &lt;strong&gt;wiped before R ever starts&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;solutions&#34;&gt;Solutions&lt;/h2&gt;
&lt;p&gt;Since patching Shiny Server is unnecessary, we can utilize the natural extension points provided by the login shell or R itself.&lt;/p&gt;
&lt;h3 id=&#34;solution-1-use-profile-recommended-for-user-level-shell-vars&#34;&gt;Solution 1: Use &lt;code&gt;~/.profile&lt;/code&gt; (Recommended for User-Level Shell Vars)&lt;/h3&gt;
&lt;p&gt;Since &lt;code&gt;--login&lt;/code&gt; causes the shell to source &lt;code&gt;~/.profile&lt;/code&gt; for the &lt;code&gt;shiny&lt;/code&gt; user, we can write our environment variables to &lt;code&gt;/home/shiny/.profile&lt;/code&gt; during the Docker build phase.&lt;/p&gt;
&lt;p&gt;In your &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Append environment variable to the shiny user&amp;#39;s profile&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;export SHOULD_IN_SHINY=&amp;#34;From dockerfile&amp;#34;&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /home/shiny/.profile&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;When Shiny Server executes &lt;code&gt;su shiny --login ...&lt;/code&gt;, the login shell will read &lt;code&gt;/home/shiny/.profile&lt;/code&gt; and load &lt;code&gt;SHOULD_IN_SHINY&lt;/code&gt; into the environment right before starting the R process.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;solution-2-use-renviron-recommended-for-r-specific-configs&#34;&gt;Solution 2: Use &lt;code&gt;~/.Renviron&lt;/code&gt; (Recommended for R-Specific Configs)&lt;/h3&gt;
&lt;p&gt;Alternatively, R automatically inspects and loads &lt;code&gt;~/.Renviron&lt;/code&gt; on startup, right after the shell environment is initialized.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.Renviron&lt;/code&gt; is purpose-built for R:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scoped strictly to R processes.&lt;/li&gt;
&lt;li&gt;Does not use shell &lt;code&gt;export&lt;/code&gt; keywords or &lt;code&gt;$&lt;/code&gt; variable expansions.&lt;/li&gt;
&lt;li&gt;Uses simple &lt;code&gt;KEY=value&lt;/code&gt; key-value pairs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In your &lt;code&gt;Dockerfile&lt;/code&gt;, set up &lt;code&gt;/home/shiny/.Renviron&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Set up .Renviron for the shiny user&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SHOULD_IN_SHINY=&amp;#34;From dockerfile&amp;#34;&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /home/shiny/.Renviron &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; chown shiny:shiny /home/shiny/.Renviron&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In your Shiny app (&lt;code&gt;app.R&lt;/code&gt; or &lt;code&gt;server.R&lt;/code&gt;), you can access it reliably:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;should_in_shiny &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Sys.getenv&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SHOULD_IN_SHINY&amp;#34;&lt;/span&gt;)
&lt;span style=&#34;color:#75715e&#34;&gt;# Returns: &amp;#34;From dockerfile&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: Shiny Server calls &lt;code&gt;su shiny --login --preserve-environment&lt;/code&gt;, causing &lt;code&gt;su&lt;/code&gt; to discard inherited environment variables (such as Docker &lt;code&gt;ENV&lt;/code&gt;) in favor of a clean login shell.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solution 1 (&lt;code&gt;~/.profile&lt;/code&gt;)&lt;/strong&gt;: Append &lt;code&gt;export KEY=&amp;quot;value&amp;quot;&lt;/code&gt; to &lt;code&gt;/home/shiny/.profile&lt;/code&gt; in your &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solution 2 (&lt;code&gt;~/.Renviron&lt;/code&gt;)&lt;/strong&gt;: Append &lt;code&gt;KEY=&amp;quot;value&amp;quot;&lt;/code&gt; to &lt;code&gt;/home/shiny/.Renviron&lt;/code&gt; in your &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both approaches integrate seamlessly with Docker builds and ensure your Shiny application receives all required configuration variables cleanly.&lt;/p&gt;
- https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ - </description>
        </item>
    
    
    
        <item>
        <title>[Nextflow] Nextflow AWS options: where they take effect</title>
        <link>https://fortune9.netlify.app/2026/07/25/nextflow-aws-options-where-they-take-effect/</link>
        <pubDate>Sat, 25 Jul 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/07/25/nextflow-aws-options-where-they-take-effect/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/07/25/nextflow-aws-options-where-they-take-effect/ -&lt;p&gt;When running Nextflow pipelines on AWS Batch, you might expect that changing S3 configuration options under &lt;code&gt;aws.client.*&lt;/code&gt; (such as &lt;code&gt;maxConnections&lt;/code&gt;) would tune the data transfer performance during task input/output staging. However, Nextflow&amp;rsquo;s documentation is unclear about how these options affect S3 data transfer. You might change parameters like &lt;code&gt;aws.client.maxConnections&lt;/code&gt; only to find they have absolutely no effect on input data staging by Nextflow tasks.&lt;/p&gt;
&lt;p&gt;By leveraging AI&amp;rsquo;s power to analyze the Nextflow codebase, I finally dug into the code and figured out why: S3 data transfers actually run across two entirely different environments—the &lt;strong&gt;Head Node&lt;/strong&gt; running Nextflow and the &lt;strong&gt;EC2 Execution Instances&lt;/strong&gt; (started by AWS Batch for running individual tasks).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; that this post was written based on the information given by AI agent, so please let me know if you find any mistakes or inaccuracies in the content. I will update the post accordingly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;the-two-s3-transfer-layers&#34;&gt;The Two S3 Transfer Layers&lt;/h3&gt;
&lt;p&gt;Data transfer processes are divided into two separate groups based on different running machines:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Environment (Machine)&lt;/th&gt;
&lt;th&gt;Run by&lt;/th&gt;
&lt;th&gt;Governed by&lt;/th&gt;
&lt;th&gt;Key Operations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Head Node Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Head Node (Nextflow host)&lt;/td&gt;
&lt;td&gt;Nextflow JVM (AWS SDK v2 CRT)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client.*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;publishDir&lt;/code&gt;, writing &lt;code&gt;.command.*&lt;/code&gt;, &lt;code&gt;bin/&lt;/code&gt; upload, gathering outputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-Container Staging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EC2 Instance (Batch container)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3&lt;/code&gt; CLI / &lt;code&gt;s5cmd&lt;/code&gt; (Classic)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch.*&lt;/code&gt; + subset of &lt;code&gt;aws.client.*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Staging inputs, unstaging outputs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Note: Options set for one layer do not propagate to the other unless explicitly translated.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;how-staging-works-in-classic-mode&#34;&gt;How Staging Works in Classic Mode&lt;/h3&gt;
&lt;p&gt;Normally, Nextflow writes the staging script from the head node, but the EC2 container does the actual staging work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;AwsBatchScriptLauncher&lt;/code&gt; embeds bash snippets into the &lt;code&gt;.command.run&lt;/code&gt; script.&lt;/li&gt;
&lt;li&gt;Inside the container, the Batch executor downloads the runner script: &lt;code&gt;aws s3 cp .command.run - | bash&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The script defines download/upload functions (&lt;code&gt;nxf_s3_download&lt;/code&gt;, &lt;code&gt;nxf_s3_upload&lt;/code&gt;) using the container&amp;rsquo;s &lt;code&gt;aws&lt;/code&gt; CLI.&lt;/li&gt;
&lt;li&gt;Input files are staged: &lt;code&gt;downloads=(...)&lt;/code&gt; lists files, and &lt;code&gt;nxf_parallel&lt;/code&gt; runs transfers concurrently.&lt;/li&gt;
&lt;li&gt;The container runs the user task.&lt;/li&gt;
&lt;li&gt;Output files are unstaged: &lt;code&gt;uploads=(...)&lt;/code&gt; runs to push results back to S3.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&#34;which-options-reach-the-ec2-container&#34;&gt;Which Options Reach the EC2 Container?&lt;/h3&gt;
&lt;p&gt;Most &lt;code&gt;aws.client.*&lt;/code&gt; settings only configure the head node&amp;rsquo;s JVM S3 client and &lt;strong&gt;never&lt;/strong&gt; reach the EC2 instance. The following options do &lt;strong&gt;not&lt;/strong&gt; affect in-container staging in classic mode:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;minimumPartSize&lt;/code&gt;, &lt;code&gt;multipartThreshold&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxConnections&lt;/code&gt;, &lt;code&gt;maxConcurrency&lt;/code&gt;, &lt;code&gt;targetThroughputInGbps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxDownloadHeapMemory&lt;/code&gt;, &lt;code&gt;maxNativeMemory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxErrorRetry&lt;/code&gt;, &lt;code&gt;connectionTimeout&lt;/code&gt;, &lt;code&gt;socketTimeout&lt;/code&gt;, &lt;code&gt;proxy*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, the only options that affect the EC2 instance&amp;rsquo;s S3 CLI transfers are those explicitly translated into CLI flags by &lt;code&gt;S3BashLib&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Source Scope&lt;/th&gt;
&lt;th&gt;Effect on EC2 Instance / Staging Script&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxParallelTransfers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Caps concurrency in the &lt;code&gt;nxf_parallel&lt;/code&gt; script function (default: 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delayBetweenAttempts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Controls the retry sleep duration in &lt;code&gt;nxf_cp_retry&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxTransferAttempts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets retry count in &lt;code&gt;nxf_cp_retry&lt;/code&gt; and defines &lt;code&gt;AWS_MAX_ATTEMPTS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cliPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines the path to the &lt;code&gt;aws&lt;/code&gt; binary and sets the &lt;code&gt;--region&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retryMode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Injects the &lt;code&gt;AWS_RETRY_MODE&lt;/code&gt; environment variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s5cmdPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses &lt;code&gt;s5cmd&lt;/code&gt; instead of &lt;code&gt;aws s3&lt;/code&gt; inside the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;forceGlacierTransfer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--force-glacier-transfer&lt;/code&gt; to &lt;code&gt;aws s3 cp&lt;/code&gt; downloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storageClass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--storage-class&lt;/code&gt; to &lt;code&gt;aws s3 cp&lt;/code&gt; uploads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storageEncryption&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--sse&lt;/code&gt; to &lt;code&gt;aws s3 cp&lt;/code&gt; uploads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storageKmsKeyId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--sse-kms-key-id&lt;/code&gt; to &lt;code&gt;aws s3 cp&lt;/code&gt; uploads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s3Acl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--acl&lt;/code&gt; to &lt;code&gt;aws s3 cp&lt;/code&gt; uploads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;requesterPays&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;--request-payer requester&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws.client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Appends the &lt;code&gt;--debug&lt;/code&gt; flag to CLI calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&#34;controlling-transfer-rates&#34;&gt;Controlling Transfer Rates&lt;/h3&gt;
&lt;p&gt;A common issue on small EC2 instances is memory or network exhaustion during staging. Currently, &lt;strong&gt;Nextflow does not provide an option to inject arbitrary CLI flags&lt;/strong&gt; (such as &lt;code&gt;--max-bandwidth&lt;/code&gt;) into the generated S3 CLI commands.&lt;/p&gt;
&lt;p&gt;If you need fine-grained transfer rate control to prevent instances from crashing, you must configure this outside of Nextflow (for instance, via custom S3 configurations or instance-level limits on the execution host).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;a-note-on-fusion-file-system&#34;&gt;A Note on Fusion file system&lt;/h3&gt;
&lt;p&gt;When Fusion is enabled, &lt;strong&gt;no&lt;/strong&gt; &lt;code&gt;aws s3&lt;/code&gt; CLI staging occurs inside the container. Fusion mounts S3 directly as a POSIX filesystem on the EC2 instance and does its own transfers. Staging is therefore governed by Fusion/wave settings instead of standard Nextflow &lt;code&gt;aws&lt;/code&gt; S3 CLI configs.&lt;/p&gt;
- https://fortune9.netlify.app/2026/07/25/nextflow-aws-options-where-they-take-effect/ - </description>
        </item>
    
    
    
        <item>
        <title>[AI] Maximizing Your Claude Code Productivity: Key Takeaways and Resources</title>
        <link>https://fortune9.netlify.app/2026/07/18/maximizing-your-claude-code-productivity/</link>
        <pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/07/18/maximizing-your-claude-code-productivity/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/07/18/maximizing-your-claude-code-productivity/ -&lt;p&gt;I recently finished the LinkedIn Learning course &lt;a href=&#34;https://www.linkedin.com/learning/maximize-your-claude-code-programming-productivity&#34;&gt;Maximize Your Claude Code Programming Productivity&lt;/a&gt;. It was packed with actionable strategies, advanced configurations, and architectural paradigms that elevate coding with AI from simple code generation to advanced project execution.&lt;/p&gt;
&lt;p&gt;To help me and others revisit these key concepts, I&amp;rsquo;ve summarized the core takeaways, tools, and resources from the course.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-blended-project-management--spec-driven-development-sdd&#34;&gt;1. Blended Project Management &amp;amp; Spec-Driven Development (SDD)&lt;/h2&gt;
&lt;p&gt;One of the biggest paradigm shifts in AI-assisted development is the transition from &lt;strong&gt;sequential&lt;/strong&gt; to &lt;strong&gt;blended&lt;/strong&gt; project management.&lt;/p&gt;
&lt;p&gt;Traditionally, product development follows a linear sequence:
&lt;strong&gt;Product Goal&lt;/strong&gt; → &lt;strong&gt;Design&lt;/strong&gt; → &lt;strong&gt;Engineering&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With AI agents, these boundaries blur. Product management, design, and engineering run in tight, parallel feedback loops.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn more about this paradigm shift in Anthropic&amp;rsquo;s blog post: &lt;a href=&#34;https://claude.com/blog/product-management-on-the-ai-exponential&#34;&gt;Product Management on the AI Exponential&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;spec-driven-development-sdd&#34;&gt;Spec-Driven Development (SDD)&lt;/h3&gt;
&lt;p&gt;SDD is a methodology that emphasizes using machine-readable or highly structured specifications to guide the AI agent&amp;rsquo;s development process. Instead of prompting the agent ad-hoc, you write a thorough specification first.&lt;/p&gt;
&lt;p&gt;Several open standards exist for organizing these specifications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kiro Spec Doc&lt;/strong&gt;: A structured spec format designed for clarity. &lt;a href=&#34;https://kiro.dev/docs/specs/&#34;&gt;Kiro Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Spec-Kit&lt;/strong&gt;: A toolkit for creating structured project specs. &lt;a href=&#34;https://github.com/github/spec-kit&#34;&gt;GitHub spec-kit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open Spec&lt;/strong&gt;: A collaborative standard for open-source project specs. &lt;a href=&#34;https://openspec.dev/&#34;&gt;OpenSpec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro-Tip&lt;/strong&gt;: You can create a custom agent &lt;strong&gt;skill&lt;/strong&gt; based on these SDD principles to help automate draft spec generation for your new projects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-agent--subagent-architecture&#34;&gt;2. Agent &amp;amp; Subagent Architecture&lt;/h2&gt;
&lt;p&gt;As tasks become more complex, a single agent&amp;rsquo;s context window can quickly become cluttered, leading to degradation in reasoning.&lt;/p&gt;
&lt;h3 id=&#34;spawning-subagents&#34;&gt;Spawning Subagents&lt;/h3&gt;
&lt;p&gt;A main agent can spawn multiple subagents to handle specific subtasks concurrently or in isolation.&lt;/p&gt;
&lt;p&gt;Why use subagents?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preserve Context&lt;/strong&gt;: Keep the main agent&amp;rsquo;s session clean. All the verbose exploration, testing, and debugging output happens in the subagent&amp;rsquo;s session.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce Constraints&lt;/strong&gt;: Restrict the tools available to a subagent (e.g., read-only access for research).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specialized Behavior&lt;/strong&gt;: Tailor system prompts to a narrow domain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control Costs&lt;/strong&gt;: Route simpler subtasks to cheaper, faster models (like Claude 3.5 Haiku / Gemini Flash Lite) while keeping the main coordinator on a more capable model.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;subagent-configuration&#34;&gt;Subagent Configuration&lt;/h3&gt;
&lt;p&gt;For standard Claude Code, custom subagent configurations are stored in &lt;code&gt;~/.claude/agents/&lt;/code&gt; in markdown format. More details are available in the &lt;a href=&#34;https://code.claude.com/docs/en/sub-agents&#34;&gt;Claude Code Sub-agents Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For Google&amp;rsquo;s &lt;strong&gt;Antigravity CLI&lt;/strong&gt; (AGY), you can define subagents using an &lt;code&gt;agent.md&lt;/code&gt; file in two scopes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspace-Specific&lt;/strong&gt;: &lt;code&gt;{workspace}/.agents/agents/{agent_name}/agent.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global Scope&lt;/strong&gt;: &lt;code&gt;~/.gemini/config/agents/{agent_name}/agent.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-extending-claude-code-with-mcp&#34;&gt;3. Extending Claude Code with MCP&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; allows Claude Code to securely connect with external tools, APIs, and data sources. Here are some of the most useful MCP servers covered in the course:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub MCP Server&lt;/strong&gt;: Manage repositories, create and search issues, handle pull requests, and perform code reviews directly from the terminal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Datadog MCP Server&lt;/strong&gt;: Monitor application health, query logs, trigger metrics alerts, and analyze performance data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linear MCP Server&lt;/strong&gt;: Seamlessly sync development tasks, create issues, update task statuses, and assign tickets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notion MCP Server&lt;/strong&gt;: Organize documentation, update pages, search notes databases, and collaborate with your team.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-fine-tuning-agent-skills&#34;&gt;4. Fine-Tuning Agent Skills&lt;/h2&gt;
&lt;p&gt;Skills allow you to package procedural knowledge, workflows, and templates for the agent. Sometimes you want fine-grained control over how the agent uses these skills:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;disable-model-invocation: true&lt;/code&gt;&lt;/strong&gt;: The skill is loaded and visible to the user, but the agent cannot invoke it automatically. The user must explicitly tell the agent to run it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;user-invocable: false&lt;/code&gt;&lt;/strong&gt;: Hides the skill from the user interface, but allows the agent to invoke it when it determines it is necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-routines-loops-and-hooks&#34;&gt;5. Routines, Loops, and Hooks&lt;/h2&gt;
&lt;p&gt;Automation in Claude Code goes beyond individual prompts through loops and hook lifecycles.&lt;/p&gt;
&lt;h3 id=&#34;goal-driven-loops-goal&#34;&gt;Goal-Driven Loops (&lt;code&gt;/goal&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;You can use the &lt;code&gt;/goal&lt;/code&gt; command to specify a target outcome. The agent runs in a continuous loop, inspecting command outputs, writing code, and self-correcting until the objective is achieved.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: Keep the goal specific, achievable, and measurable (e.g., &amp;ldquo;Fix all linting errors in the src/ directory and ensure tests pass&amp;rdquo;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;lifecycle-hooks&#34;&gt;Lifecycle Hooks&lt;/h3&gt;
&lt;p&gt;Hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific lifecycle points.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: See &lt;a href=&#34;https://code.claude.com/docs/en/Hooks&#34;&gt;Claude Code Hooks Guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example Use Case&lt;/strong&gt;: You can set up a hook that intercepts commands (e.g., validating critical operations before executing an &lt;code&gt;rm&lt;/code&gt; command or running a test suite before git commits).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;summary-cheat-sheet&#34;&gt;Summary Cheat Sheet&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Standard Claude Code&lt;/th&gt;
&lt;th&gt;Antigravity CLI (Gemini)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subagents Path&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/agents/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{workspace}/.agents/agents/&lt;/code&gt; &lt;!-- raw HTML omitted --&gt; &lt;code&gt;~/.gemini/config/agents/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Goal Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/loop&lt;/code&gt; or &lt;code&gt;/goal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/goal&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hooks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://code.claude.com/docs/en/Hooks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported via hooks framework&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;By incorporating &lt;strong&gt;Spec-Driven Development&lt;/strong&gt; and delegating tasks to specialized &lt;strong&gt;subagents&lt;/strong&gt;, we can write cleaner, more maintainable code while staying in control of the project lifecycle.&lt;/p&gt;
- https://fortune9.netlify.app/2026/07/18/maximizing-your-claude-code-productivity/ - </description>
        </item>
    
    
    
        <item>
        <title>[git] Git Worktree: Working on Multiple Branches Simultaneously</title>
        <link>https://fortune9.netlify.app/2026/06/27/git-worktree-working-on-multiple-branches-simultaneously/</link>
        <pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/06/27/git-worktree-working-on-multiple-branches-simultaneously/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/06/27/git-worktree-working-on-multiple-branches-simultaneously/ -&lt;p&gt;Have you ever found yourself deep in a feature branch, with multiple uncommitted changes, only to be interrupted by a critical bug report on &lt;code&gt;main&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Traditionally, you had two choices:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Stash and Switch:&lt;/strong&gt; Run &lt;code&gt;git stash&lt;/code&gt;, switch branches, fix the bug, commit, switch back, and run &lt;code&gt;git stash pop&lt;/code&gt;. This context-switching can disrupt your focus and break active local builds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clone Again:&lt;/strong&gt; Clone the repository to another folder entirely. This wastes disk space and time, duplication of the object database, and leaves you managing separate local repositories.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fortunately, Git provides a third, elegant solution: &lt;strong&gt;&lt;code&gt;git worktree&lt;/code&gt;&lt;/strong&gt;. This feature allows you to attach multiple working directories (worktrees) to a single local repository, allowing you to checkout and work on different branches concurrently.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;git-branch-vs-git-worktree&#34;&gt;Git Branch vs. Git Worktree&lt;/h2&gt;
&lt;p&gt;Here is how Git Worktree stacks up against standard branch switching and multiple repository clones:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Feature&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Standard Git Branch (Single Checkout)&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Git Worktree (Multiple Checkouts)&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Multiple Clones&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Active Branches&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Only &lt;strong&gt;one&lt;/strong&gt; branch can be checked out at a time.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Multiple&lt;/strong&gt; branches checked out simultaneously in different folders.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Multiple&lt;/strong&gt; branches in different folders.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Git Database (&lt;code&gt;.git&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Shared single instance.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Shared single instance&lt;/strong&gt; (all worktrees point to the same main &lt;code&gt;.git&lt;/code&gt; database).&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Independent copies (disjoint history and refs, duplicates database).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Disk Space&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Minimal.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Minimal&lt;/strong&gt; (only checks out the files needed for each directory).&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;High (each clone duplicates the object database).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Local Stashing Required&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Yes, if you have uncommitted work.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;No&lt;/strong&gt;; just open a new folder for the hotfix while keeping your feature work in progress.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;No.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Sequential task progression.&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;Concurrent tasks&lt;/strong&gt; (e.g., hotfixes, reviewing PRs, running tests in background).&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Separate forks or independent repos.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;how-git-worktree-works&#34;&gt;How Git Worktree Works&lt;/h2&gt;
&lt;p&gt;Rather than duplicating the entire Git history and configuration, &lt;code&gt;git worktree&lt;/code&gt; sets up a main repository with additional linked checkouts. Each worktree directory contains its own files and a special &lt;code&gt;.git&lt;/code&gt; file that points back to the main repository&amp;rsquo;s &lt;code&gt;.git&lt;/code&gt; database.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;git_worktree_diagram.jpg&#34; alt=&#34;Git Worktree Conceptual Diagram&#34;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;hands-on-working-with-a-mock-repo&#34;&gt;Hands-On: Working with a Mock Repo&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s walk through a complete, hands-on workflow to see &lt;code&gt;git worktree&lt;/code&gt; in action.&lt;/p&gt;
&lt;h3 id=&#34;step-1-create-a-mock-repository&#34;&gt;Step 1: Create a Mock Repository&lt;/h3&gt;
&lt;p&gt;First, we will set up a main repository with an initial commit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Create and initialize the project&lt;/span&gt;
mkdir my-project
cd my-project
git init

&lt;span style=&#34;color:#75715e&#34;&gt;# Add a README and commit&lt;/span&gt;
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;# My Awesome Project&amp;#34;&lt;/span&gt; &amp;gt; README.md
git add README.md
git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Initial commit&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;step-2-work-on-a-feature-branch&#34;&gt;Step 2: Work on a Feature Branch&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s start working on a new feature branch &lt;code&gt;feature/cool-stuff&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git checkout -b feature/cool-stuff
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Adding a cool feature...&amp;#34;&lt;/span&gt; &amp;gt; feature.txt
git add feature.txt
git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;added file feature.txt&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# make some new changes&lt;/span&gt;
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Adding new lines to feature.txt&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; feature.txt
&lt;span style=&#34;color:#75715e&#34;&gt;# (Leave feature.txt uncommitted or unstaged, you will get error)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;step-3-switch-to-a-hotfix-using-a-worktree&#34;&gt;Step 3: Switch to a Hotfix using a Worktree&lt;/h3&gt;
&lt;p&gt;Suddenly, a critical bug is reported in &lt;code&gt;main&lt;/code&gt;. Instead of stashing our uncommitted work in &lt;code&gt;feature/cool-stuff&lt;/code&gt;, we can create a new worktree directory &lt;code&gt;../my-project-hotfix&lt;/code&gt; specifically for the hotfix:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Create a new worktree and check it out to a new branch &amp;#39;hotfix/bug-123&amp;#39; based on &amp;#39;main&amp;#39;&lt;/span&gt;
git worktree add -b hotfix/bug-123 ../my-project-hotfix main
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This creates a new folder &lt;code&gt;my-project-hotfix&lt;/code&gt; outside of your current folder. Git automatically sets up the files, pointing them back to your main database.&lt;/p&gt;
&lt;h3 id=&#34;step-4-fix-the-bug-in-the-worktree&#34;&gt;Step 4: Fix the Bug in the Worktree&lt;/h3&gt;
&lt;p&gt;Navigate to your newly created hotfix directory and apply the fix:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;cd ../my-project-hotfix

&lt;span style=&#34;color:#75715e&#34;&gt;# Apply the fix and commit it&lt;/span&gt;
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fixing critical issue...&amp;#34;&lt;/span&gt; &amp;gt; hotfix.txt
git add hotfix.txt
git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fix critical production issue&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Because your feature work remains in &lt;code&gt;my-project&lt;/code&gt;, it is completely untouched!&lt;/p&gt;
&lt;h3 id=&#34;step-5-verify-the-worktree-structure&#34;&gt;Step 5: Verify the Worktree Structure&lt;/h3&gt;
&lt;p&gt;Go back to your main repository directory to view the list of active worktrees:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;cd ../my-project
git worktree list
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This command will list all active directories linked to the repository:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;/path/to/my-project          &amp;lt;commit-hash&amp;gt; [feature/cool-stuff]
/path/to/my-project-hotfix   &amp;lt;commit-hash&amp;gt; [hotfix/bug-123]
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;step-6-cleaning-up&#34;&gt;Step 6: Cleaning Up&lt;/h3&gt;
&lt;p&gt;Once you are done with the hotfix (e.g., after pushing it or merging it to &lt;code&gt;main&lt;/code&gt;), you can safely remove the worktree folder.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s merge the hotfix back into &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# In the hotfix worktree&lt;/span&gt;
git checkout main
git merge hotfix/bug-123
git push origin main &lt;span style=&#34;color:#75715e&#34;&gt;# optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In modern versions of Git, you can remove the worktree cleanly with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git worktree remove ../my-project-hotfix
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Alternatively, you can manually delete the folder and prune the references:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;rm -rf ../my-project-hotfix
git worktree prune
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;quick-reference-commands&#34;&gt;Quick Reference Commands&lt;/h2&gt;
&lt;p&gt;Here are the most common &lt;code&gt;git worktree&lt;/code&gt; commands:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Command&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Create a new worktree at &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; checking out &lt;code&gt;&amp;lt;branch&amp;gt;&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;git worktree add -b &amp;lt;new-branch&amp;gt; &amp;lt;path&amp;gt; &amp;lt;base-branch&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Create a new worktree at &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; with a new branch &lt;code&gt;&amp;lt;new-branch&amp;gt;&lt;/code&gt; tracking &lt;code&gt;&amp;lt;base-branch&amp;gt;&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;git worktree list&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;List all active worktrees linked to the repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;git worktree remove &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Remove a worktree directory and clean up its references.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;git worktree prune&lt;/code&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Clean up worktree information for directories that were deleted manually.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Using &lt;code&gt;git worktree&lt;/code&gt; keeps your workspace clean, speeds up switching between context, and optimizes your machine&amp;rsquo;s disk space. Give it a try on your next context switch!&lt;/p&gt;
- https://fortune9.netlify.app/2026/06/27/git-worktree-working-on-multiple-branches-simultaneously/ - </description>
        </item>
    
    
    
        <item>
        <title>[R] How to Position the Legend Inside a Plot in ggplot2</title>
        <link>https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/</link>
        <pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/ -


&lt;p&gt;By default, &lt;code&gt;ggplot2&lt;/code&gt; places the legend outside the plot area (usually on the right). However, sometimes you may want to move the legend inside the plot to save space or improve the layout. This post explores how to achieve this using &lt;code&gt;theme()&lt;/code&gt; parameters.&lt;/p&gt;
&lt;div id=&#34;example-1-basic-usage-of-legend.position&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Example 1: Basic Usage of &lt;code&gt;legend.position&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The simplest way to move a legend inside the plot is by providing a numeric vector of length two to the &lt;code&gt;legend.position&lt;/code&gt; argument in &lt;code&gt;theme()&lt;/code&gt;. These coordinates represent the relative position within the plot panel, ranging from 0 to 1. Check the
section on &lt;a href=&#34;#key-positioning-controls&#34;&gt;“Key Positioning Controls”&lt;/a&gt; at the end for a detailed explanation of the coordinate system.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)

ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme_bw() +
  theme(legend.position = c(0.8, 0.8))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example1-1.png&#34; alt=&#34;&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;example-2-fine-tuning-with-justification-and-background&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Example 2: Fine-Tuning with Justification and Background&lt;/h2&gt;
&lt;p&gt;When you place a legend inside, you often need to adjust which part of the legend box aligns with your coordinates and handle the background if it obscures data.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;legend.justification&lt;/code&gt;&lt;/strong&gt;: Controls the anchor point of the legend box. For example, &lt;code&gt;c(&#34;right&#34;, &#34;top&#34;)&lt;/code&gt; means the top-right corner of the legend box will be placed at the specified coordinates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;legend.background&lt;/code&gt;&lt;/strong&gt;: Use &lt;code&gt;element_blank()&lt;/code&gt; to make the background transparent.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme_bw() +
  theme(
    legend.position = c(1, 1),
    legend.justification = c(&amp;quot;right&amp;quot;, &amp;quot;top&amp;quot;),
    legend.background = element_blank()
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example2-1.png&#34; alt=&#34;&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;example-3-positioning-in-faceted-plots&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Example 3: Positioning in Faceted Plots&lt;/h2&gt;
&lt;p&gt;Moving the legend inside a faceted plot works similarly: the whole figure is considered as
one plot for the coordinate system. You can use the coordinates to place it within one of the empty spaces or over a specific panel.
In the following example, the legend is placed in the bottom center of the plot, and a background is added to improve readability:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  facet_wrap(~am) +
  theme_bw() +
  theme(
    legend.position = c(0.5, 0.2),
    legend.background = element_rect(fill = &amp;quot;white&amp;quot;, color = &amp;quot;grey80&amp;quot;)
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example3-1.png&#34; alt=&#34;&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;example-4-the-empty-space-trick-for-faceted-plots&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Example 4: The “Empty Space” Trick for Faceted Plots&lt;/h2&gt;
&lt;p&gt;If you have a panel which doesn’t have any data, and then it is the perfect spot to
place legend. To show this, the facet variable &lt;code&gt;carb&lt;/code&gt; has only 3 values, and we
will make a 2x2 facet grid. We will also remove the background and border of the legend.
The following example shows this:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Use 3 categories to leave the 4th spot in a 2x2 grid empty
mtcars_subset &amp;lt;- mtcars[mtcars$carb %in% c(1, 2, 4), ]

ggplot(mtcars_subset, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  facet_wrap(~carb, nrow = 2, ncol = 2) +
  theme_bw() +
  theme(
    # Coordinates c(0.75, 0.25) target the center of the empty bottom-right area
    legend.position = c(0.75, 0.25),
    legend.background = element_blank(),
    legend.justification = &amp;quot;center&amp;quot;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example4-1.png&#34; alt=&#34;&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;key-positioning-controls&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Key Positioning Controls&lt;/h2&gt;
&lt;p&gt;To master legend positioning, keep these three controls in mind:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;&lt;strong&gt;Coordinate System&lt;/strong&gt;: The coordinates &lt;code&gt;c(X, Y)&lt;/code&gt; represent a relative scale from 0 to 1 across the plot panel.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;c(0, 0)&lt;/code&gt;: Bottom-left corner&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c(1, 1)&lt;/code&gt;: Top-right corner&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c(0.5, 0.5)&lt;/code&gt;: Center of the plot&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anchor Point (&lt;code&gt;legend.justification&lt;/code&gt;)&lt;/strong&gt;: Use this to control which part of the legend box aligns with your coordinates. For example, &lt;code&gt;c(&#34;right&#34;, &#34;top&#34;)&lt;/code&gt; ensures the top-right corner of the legend box sits exactly at your target coordinate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Background Removal&lt;/strong&gt;: If your legend blocks data points, clear the legend box background using &lt;code&gt;legend.background = element_blank()&lt;/code&gt;. Alternatively, use &lt;code&gt;element_rect()&lt;/code&gt; to give it a semi-transparent or styled background.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Happy Programming!&lt;/p&gt;
&lt;/div&gt;
- https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/ - </description>
        </item>
    
    
    
        <item>
        <title>[R] How to use ggpattern to add patterns to ggplot2 plots</title>
        <link>https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/</link>
        <pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/ -


&lt;p&gt;Adding patterns to plots is a great way to improve accessibility (making plots colorblind-friendly) and to add an extra dimension of information. The &lt;code&gt;ggpattern&lt;/code&gt; package provides a rich set of tools to achieve this in &lt;code&gt;ggplot2&lt;/code&gt;.&lt;/p&gt;
&lt;div id=&#34;basic-example-with-mtcars&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Basic Example with mtcars&lt;/h2&gt;
&lt;p&gt;Let’s start with a barplot using the &lt;code&gt;mtcars&lt;/code&gt; dataset. We will map &lt;code&gt;cyl&lt;/code&gt; to the x-axis, &lt;code&gt;mpg&lt;/code&gt; to the y-axis, use &lt;code&gt;gear&lt;/code&gt; for the fill color, and &lt;code&gt;am&lt;/code&gt; for the pattern.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Prepare data
df &amp;lt;- mtcars %&amp;gt;%
  mutate(
    cyl = factor(cyl),
    gear = factor(gear),
    am = factor(am, labels = c(&amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot;))
  )

# Create barplot
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    color = &amp;quot;black&amp;quot;,
    pattern_fill = &amp;quot;black&amp;quot;,
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 1.5
  ) +
  scale_pattern_manual(values = c(&amp;quot;stripe&amp;quot;, &amp;quot;crosshatch&amp;quot;)) +
  labs(
    title = &amp;quot;MPG by Cylinders, Gear, and Transmission&amp;quot;,
    subtitle = &amp;quot;Patterns representing transmission type&amp;quot;,
    x = &amp;quot;Cylinders&amp;quot;,
    y = &amp;quot;Miles Per Gallon (MPG)&amp;quot;,
    fill = &amp;quot;Gears&amp;quot;,
    pattern = &amp;quot;Transmission&amp;quot;
  ) +
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/basic-example-1.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;exploring-key-parameters&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Exploring Key Parameters&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ggpattern&lt;/code&gt; package allows for fine-grained control over how patterns are rendered. Here are some of the most important parameters:&lt;/p&gt;
&lt;div id=&#34;pattern_key_scale_factor&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;1. pattern_key_scale_factor&lt;/h3&gt;
&lt;p&gt;This parameter scales the pattern density specifically in the legend. Without it, patterns in the legend can often be hard to see.
Reduce the value will make it easier fit into the legend keys.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p_base &amp;lt;- ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    color = &amp;quot;black&amp;quot;,
    pattern_spacing = 0.02
  ) +
  theme_minimal() +
  labs(title = &amp;quot;Default vs Scaled Legend&amp;quot;)

# Default legend
p1 &amp;lt;- p_base + labs(subtitle = &amp;quot;Default (key_scale_factor = 1)&amp;quot;)

# Scaled legend
p2 &amp;lt;- p_base + 
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    color = &amp;quot;black&amp;quot;,
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 0.5
  ) +
  labs(subtitle = &amp;quot;Scaled (key_scale_factor = 0.5)&amp;quot;)

p1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/scale-factor-1.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/scale-factor-2.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;pattern_spacing-and-pattern_density&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;2. pattern_spacing and pattern_density&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pattern_spacing&lt;/code&gt; controls the distance between pattern elements (lower = denser), while &lt;code&gt;pattern_density&lt;/code&gt; controls the coverage (higher = denser).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Spacing effect
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    pattern_spacing = 0.2, # Wider spacing
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &amp;quot;Effect of pattern_spacing = 0.5 (Sparser)&amp;quot;) +
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/spacing-density-1.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Density effect
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    pattern_density = 0.8, # Higher density
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &amp;quot;Effect of pattern_density = 0.8 (Denser)&amp;quot;) +
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/spacing-density-2.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;pattern_fill&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;3. pattern_fill&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pattern_fill&lt;/code&gt; sets the color of the pattern elements themselves.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    pattern_fill = &amp;quot;white&amp;quot;, # White pattern lines
    pattern_density = 0.5, # make it bigger so that the color is easier to see
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &amp;quot;Effect of pattern_fill = &amp;#39;white&amp;#39;&amp;quot;) +
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/pattern-fill-1.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;pattern_shape&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;4. pattern_shape&lt;/h3&gt;
&lt;p&gt;For geometric patterns like &lt;code&gt;regular_polygon&lt;/code&gt;, &lt;code&gt;pattern_shape&lt;/code&gt; controls the number of sides.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern_shape=gear),
    pattern = &amp;quot;regular_polygon&amp;quot;,
    stat = &amp;quot;identity&amp;quot;,
    position = &amp;quot;dodge&amp;quot;,
    pattern_size = 0.5, # stroke line width
    pattern_density = 0.5, # increase the shape size
    pattern_spacing = 0.03,
    pattern_key_scale_factor = 1.5
  ) +
  scale_pattern_shape_manual(values = c(&amp;quot;3&amp;quot;=&amp;quot;circle&amp;quot;, &amp;quot;4&amp;quot;=&amp;quot;convex6&amp;quot;, &amp;quot;5&amp;quot;=&amp;quot;square&amp;quot;)) + # manually set shapes
  labs(title = &amp;quot;Effect of mapping pattern_shape to gear&amp;quot;) +
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/pattern-shape-1.png&#34; alt=&#34;&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;troubleshooting&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;p&gt;If you encounter issues installing or loading &lt;code&gt;ggpattern&lt;/code&gt; on Linux, you may need to install the following system dependencies:&lt;/p&gt;
&lt;pre class=&#34;bash&#34;&gt;&lt;code&gt;sudo apt-get install libudunits2-dev
sudo apt install libproj22
sudo apt-get install libgdal-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
- https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/ - </description>
        </item>
    
    
    
        <item>
        <title>Understanding the Z-Algorithm for Efficient String Matching</title>
        <link>https://fortune9.netlify.app/2026/05/22/z-algorithm-for-string-matching/</link>
        <pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/05/22/z-algorithm-for-string-matching/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/05/22/z-algorithm-for-string-matching/ -&lt;p&gt;String matching is a fundamental problem in computer science. Given a text &lt;code&gt;T&lt;/code&gt; and a pattern &lt;code&gt;P&lt;/code&gt;, how can we efficiently find all occurrences of &lt;code&gt;P&lt;/code&gt; in &lt;code&gt;T&lt;/code&gt;? While naive approaches take &lt;code&gt;O(n * m)&lt;/code&gt; time, the &lt;strong&gt;Z-algorithm&lt;/strong&gt; allows us to solve this in &lt;strong&gt;O(n + m)&lt;/strong&gt; time.&lt;/p&gt;
&lt;h3 id=&#34;the-problem-matching-two-strings&#34;&gt;The Problem: Matching Two Strings&lt;/h3&gt;
&lt;p&gt;Imagine we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pattern (P):&lt;/strong&gt; &lt;code&gt;aabcaab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text (T):&lt;/strong&gt; &lt;code&gt;baabaabcaab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We want to find where &lt;code&gt;aabcaab&lt;/code&gt; occurs in the text. The Z-algorithm approaches this by constructing a new string &lt;code&gt;S = P + $ + T&lt;/code&gt; (where &lt;code&gt;$&lt;/code&gt; is a separator not present in &lt;code&gt;P&lt;/code&gt; or &lt;code&gt;T&lt;/code&gt;) and computing the &lt;strong&gt;Z-array&lt;/strong&gt; for &lt;code&gt;S&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;what-is-the-z-array&#34;&gt;What is the Z-Array?&lt;/h3&gt;
&lt;p&gt;For a string &lt;code&gt;S&lt;/code&gt; of length &lt;code&gt;n&lt;/code&gt;, the Z-array is an array of length &lt;code&gt;n&lt;/code&gt; where &lt;code&gt;Z[i]&lt;/code&gt; is the length of the longest substring starting at &lt;code&gt;S[i]&lt;/code&gt; which is also a prefix of &lt;code&gt;S&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example for &lt;code&gt;S = &lt;/code&gt; &lt;code&gt;aabcaab&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;Index: 0 1 2 3 4 5 6
String: a a b c a a b
Z-val: 0 1 0 0 3 1 0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Z[0]&lt;/code&gt; is usually defined as 0 or &lt;code&gt;n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Z[4] = 3&lt;/code&gt; because &amp;ldquo;aab&amp;rdquo; at index 4 matches the prefix &amp;ldquo;aab&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;why-the-z-algorithm-is-fast-om--n&#34;&gt;Why the Z-Algorithm is Fast: O(m + n)&lt;/h3&gt;
&lt;p&gt;The naive computation of the Z-array still takes &lt;code&gt;O(n^2)&lt;/code&gt;. The Z-algorithm achieves &lt;code&gt;O(n)&lt;/code&gt; by maintaining a &lt;strong&gt;Z-box [L, R]&lt;/strong&gt;, which is the rightmost interval &lt;code&gt;[L, R]&lt;/code&gt; such that &lt;code&gt;S[L..R]&lt;/code&gt; is a prefix of &lt;code&gt;S&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By reusing previously computed &lt;code&gt;Z&lt;/code&gt; values, we avoid redundant character comparisons. How?&lt;/p&gt;
&lt;h3 id=&#34;the-mirroring-strategy-the-heart-of-the-algorithm&#34;&gt;The Mirroring Strategy: The Heart of the Algorithm&lt;/h3&gt;
&lt;p&gt;The most critical part of the algorithm is when we compute &lt;code&gt;Z[i]&lt;/code&gt; and &lt;code&gt;i&lt;/code&gt; falls within the current Z-box &lt;code&gt;[L, R]&lt;/code&gt; (&lt;code&gt;L &amp;lt;= i &amp;lt;= R&lt;/code&gt;).&lt;/p&gt;
&lt;h4 id=&#34;the-setup&#34;&gt;The Setup&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;Prefix: [0 . . . . . R-L]
          |         |
          V         V
String: [L . . i . . R] . . .
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Since &lt;code&gt;S[L..R]&lt;/code&gt; matches &lt;code&gt;S[0..R-L]&lt;/code&gt;, any substring starting at &lt;code&gt;i&lt;/code&gt; inside the Z-box has a &amp;ldquo;mirror&amp;rdquo; image starting at &lt;code&gt;k = i - L&lt;/code&gt; in the prefix.&lt;/p&gt;
&lt;h4 id=&#34;case-1-zk-is-contained-within-the-z-box-zk--r---i--1&#34;&gt;Case 1: Z[k] is contained within the Z-box (Z[k] &amp;lt; R - i + 1)&lt;/h4&gt;
&lt;p&gt;If the match at the mirror position &lt;code&gt;k&lt;/code&gt; doesn&amp;rsquo;t reach the boundary &lt;code&gt;R&lt;/code&gt;, we know &lt;code&gt;Z[i]&lt;/code&gt; must be exactly &lt;code&gt;Z[k]&lt;/code&gt;. No new comparisons are needed!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;       k       R-L
       |       |
Prefix: [a b c] . . .
         \
          \ (mirror)
           \
String: . . [a b c] . . .
             |     |
             i     R
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;case-2-zk-reaches-or-exceeds-the-boundary-zk--r---i--1&#34;&gt;Case 2: Z[k] reaches or exceeds the boundary (Z[k] &amp;gt;= R - i + 1)&lt;/h4&gt;
&lt;p&gt;If the match at &lt;code&gt;k&lt;/code&gt; goes beyond &lt;code&gt;R&lt;/code&gt;, we only know that &lt;code&gt;S[i..R]&lt;/code&gt; matches the prefix. We must manually check characters starting from &lt;code&gt;R+1&lt;/code&gt; to see if the match extends further.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;       k       R-L
       |       |
Prefix: [a b c d e f]
         \
          \ (mirror)
           \
String: . . [a b c d ? ?]
             |     |
             i     R
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Once we find the new boundary, we update &lt;code&gt;[L, R]&lt;/code&gt; to &lt;code&gt;[i, new R]&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;python-implementation&#34;&gt;Python Implementation&lt;/h3&gt;
&lt;p&gt;Here is a concise Python implementation of the Z-algorithm:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;compute_z&lt;/span&gt;(s):
    n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; len(s)
    z &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; n
    l, r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, n):
        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; r:
            z[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; min(r &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, z[i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; l]) &lt;span style=&#34;color:#75715e&#34;&gt;# note the index is i - l (letter l), not i - 1&lt;/span&gt;
        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; z[i] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; s[z[i]] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; s[i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; z[i]]:
            z[i] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; z[i] &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; r:
            l, r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; i, i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; z[i] &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; z
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;how-to-find-matches-p-in-t&#34;&gt;How to Find Matches: P in T&lt;/h3&gt;
&lt;p&gt;To find all occurrences of a pattern &lt;code&gt;P&lt;/code&gt; in a text &lt;code&gt;T&lt;/code&gt;, we use the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Construct a combined string:&lt;/strong&gt; Create a new string by joining the pattern and the text with a unique separator that appears in neither: &lt;code&gt;Combined = P + &amp;quot;#&amp;quot; + T&lt;/code&gt; (here we use &lt;code&gt;#&lt;/code&gt; as the separator).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compute the Z-array:&lt;/strong&gt; Run the Z-algorithm on this combined string.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identify matches:&lt;/strong&gt; Iterate through the Z-array values corresponding to the text portion (indices after the &lt;code&gt;#&lt;/code&gt; separator).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check for pattern length:&lt;/strong&gt; If any &lt;code&gt;Z[i]&lt;/code&gt; is equal to the length of &lt;code&gt;P&lt;/code&gt;, it means a match of the pattern &lt;code&gt;P&lt;/code&gt; starts at that position in the text.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The separator is crucial because it ensures that the &amp;ldquo;match&amp;rdquo; does not extend beyond the length of the pattern itself, preventing false positives that might span across the boundary between &lt;code&gt;P&lt;/code&gt; and &lt;code&gt;T&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The Z-algorithm&amp;rsquo;s mirroring strategy is a brilliant example of using space (the Z-array) to save time. By keeping track of the furthest reach (&lt;code&gt;R&lt;/code&gt;), we ensure that each character is effectively compared only a constant number of times.&lt;/p&gt;
&lt;p&gt;For a more detailed breakdown and implementation details, check out the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CP-Algorithms: &lt;a href=&#34;https://cp-algorithms.com/string/z-function.html&#34;&gt;https://cp-algorithms.com/string/z-function.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GeeksforGeeks: &lt;a href=&#34;https://www.geeksforgeeks.org/z-algorithm-linear-time-pattern-searching-algorithm/&#34;&gt;https://www.geeksforgeeks.org/z-algorithm-linear-time-pattern-searching-algorithm/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
- https://fortune9.netlify.app/2026/05/22/z-algorithm-for-string-matching/ - </description>
        </item>
    
    
    
        <item>
        <title>[R] How to change legend key size in ggplot2</title>
        <link>https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/</link>
        <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
        
        <guid>https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/</guid>
        <description>Zhenguo Zhang&#39;s Blog https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/ -


&lt;p&gt;In &lt;code&gt;ggplot2&lt;/code&gt;, the legend keys (the symbols next to the labels) usually inherit their size and appearance from the layers in the plot. For example, if you have a scatter plot with very small points, the legend keys will also be very small, making them hard to see.&lt;/p&gt;
&lt;p&gt;To fix this without changing the size of the points in the actual plot, you can use the &lt;code&gt;override.aes&lt;/code&gt; argument within &lt;code&gt;guide_legend()&lt;/code&gt;.&lt;/p&gt;
&lt;div id=&#34;the-problem-tiny-legend-keys&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;The Problem: Tiny Legend Keys&lt;/h3&gt;
&lt;p&gt;When plotting many data points, we often decrease the &lt;code&gt;size&lt;/code&gt; to avoid overplotting. However, this makes the legend keys tiny:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)

p &amp;lt;- ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
  geom_point(size = 0.5) + 
  theme_minimal() +
  labs(title = &amp;quot;Tiny Legend Keys&amp;quot;)

p&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/index_files/figure-html/tiny-legend-1.png&#34; alt=&#34;&#34; width=&#34;480&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;the-solution-using-override.aes&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;The Solution: Using &lt;code&gt;override.aes&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;By using &lt;code&gt;guides()&lt;/code&gt;, we can “override” the aesthetics of the legend. This allows us to keep the small points in the plot while making the legend keys large and clear.&lt;/p&gt;
&lt;p&gt;If your legend is for the &lt;strong&gt;color&lt;/strong&gt; aesthetic:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p_fixed &amp;lt;- p + 
  guides(color = guide_legend(override.aes = list(size = 5))) +
  labs(title = &amp;quot;Fixed Legend Keys (Size 5)&amp;quot;)

p_fixed&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/index_files/figure-html/fixed-legend-1.png&#34; alt=&#34;&#34; width=&#34;480&#34; /&gt;&lt;/p&gt;
&lt;p&gt;If you were using &lt;strong&gt;fill&lt;/strong&gt; (e.g., in a bar plot or boxplot), you would simply change &lt;code&gt;color&lt;/code&gt; to &lt;code&gt;fill&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;guides(fill = guide_legend(override.aes = list(size = 5)))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;adjusting-legend-text-and-title-size&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Adjusting Legend Text and Title Size&lt;/h3&gt;
&lt;p&gt;Note that &lt;code&gt;override.aes&lt;/code&gt; only affects the symbols inside the legend. To change the size of the labels or the legend title, you should use the &lt;code&gt;theme()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p_fixed + 
  theme(
    legend.text = element_text(size = 12),   # Legend labels
    legend.title = element_text(size = 14, face = &amp;quot;bold&amp;quot;) # Legend title
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/index_files/figure-html/theme-adjust-1.png&#34; alt=&#34;&#34; width=&#34;480&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;summary&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;guides(color = guide_legend(override.aes = list(size = ...)))&lt;/code&gt; to change the &lt;strong&gt;symbol size&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;theme(legend.text = element_text(size = ...))&lt;/code&gt; to change the &lt;strong&gt;text size&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
- https://fortune9.netlify.app/2026/04/27/r-how-to-change-legend-key-size-in-ggplot2/ - </description>
        </item>
    
    
  </channel>
</rss> 