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
- but can do
# 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.