@jialin.huang
FRONT-ENDBACK-ENDNETWORK, HTTPOS, COMPUTERCLOUD, AWS, Docker
To live is to risk it all Otherwise you are just an inert chunk of randomly assembled molecules drifting wherever the Universe blows you

© 2024 jialin00.com

Original content since 2022

back
RSS

Preventing Unexpected Effects of Child Shells in Bash

WHY

In Bash scripting, the creation of child shells, often through pipelines or certain command structures, can lead to unexpected behavior in variable management.

When having command like find ... | ..., a child shell is created. Child processes gets readonly copies from the parent shell's memory. Any variable changes in the child shell trigger Copy-on-Write (CoW), creating a separate memory copy for that child.

However, unmodified variables remain in shared memory.

#!/bin/bash

# ...
processed_count=0
updated_count=0


find "$directory" -name "*.jpeg" -type f | while read -r file; do
    # ...
    ((processed_count++))
done

echo "Total "$processed_count" files, Update "$updated_count"  files"

The last line shows processed_count and updated_count still remain the same as initial value. And I don’t want that result.

Solution

This approach ensures that the loop runs in the current shell, allowing variables to be updated correctly.

#!/bin/bash

# ...

while read -r file; do
    # ...
    ((processed_count++))
done < <(find "$directory" -name "*.jpeg" -type f)

(..) v.s. ((..))

Single Parentheses ()

  • Creates a child shell (child process)
  • ; works as commands separator

Double Parentheses (())

  • It only does arithmetic expression
  • works in the current shell
  • ; causes error
  • Cannot contain many commands together command1; command2; command3;
    • but can do math expression 1, math expression 2
# works
(var=20; echo "In subshell: $var")

# failed, syntax error, it only does "arithmetic expression"
((var=20; echo "In subshell: $var"))

# works, just many arithmetic expressions
((var=20, temp=var*2))

 ((x = y + 1))
 # equal to 
 x=$((y + 1))

Why need (()) to specify an expression even when it's already on the current shell

# no child
while read -r file; do
    # ...
    ((processed_count++))
done < <(find "$directory" -name "*.html" -type f)

prevents ambiguity

make it elegant

Child Shell vs. Child Process

  • every child shell is a child process
  • not every child process is necessarily a child shell
  • Child shells are a specific type of child process.

EOF