Bash: Piped `while-read' loop starts subshell
Appearance
Problem
Bash can sometimes start a subshell in a PIPED `while-read' loop. This causes variables introduced within the while-read loop to disappear. For example:
echo nothing | while read line; do
foo=bar
echo foo1: $foo
done
echo foo2: $foo
# Example output:
# foo1: bar
# foo2: (shouldn't be empty)Environment
- Debian 4.0
- GNU bash, version 3.1.17(1)-release (i486-pc-linux-gnu)
Solutions
Solution 1. Store loop result (preferred)
By encapsulating the while-loop as a compound command ((list) or { list; } makes no difference?), the result of the while-loop can be echoed and catched by an outer variable:
unset -v foo
foo=$(
echo nothing | {
while read line; do
foo=bar
done
echo $foo
}
)
echo foo2: $foo
# Example output:
# foo2: bar (ok)Solution 2. Redirected while loop (not POSIX compatible)
Rewrite the script as a REDIRECTED `while-read' loop. For example, using process substitution:
while read line; do
foo=bar
echo foo1: $foo
done < <(echo nothing)
echo foo2: $foo
# Example output:
# foo1: bar
# foo2: barHowever, when POSIX-mode is enabled (set -o posix), bash gives an error:
bash: syntax error near unexpected token `<'
See also
- Advanced Bash-Scripting Guide: Chapter 19. I/O Redirection
- The problem is mentioned within comments of the "Advanced Bash-Scripting Guide"
- Bash: Gotchas
- More bash code which was non-intuitive to me
Journal
20070523
- Altering a variable outside the scope of a loop influenced by a subshell - nion's blog
- Blog entry (April 8, 2007) about the problem, with useful comments
- Advanced Bash-Scripting Guide - Gotchas
- See example 31-3: "Piping echo output to a read may produce unexpected results. In this scenario, the read acts as if it were running in a subshell."
20070525
- SHELLdorado Newsletter 1/2005 - April 30th, 2005
- See newsletter item: "Shell Tip: How to read a file line-by-line"
20071214
- Altering a variable outside the scope of a loop influenced by a subshell
- Blog article with proposed solution to work in bash POSIX mode, but it doesn't work:
set -o posix
foo=
echo nothing | {
while read line; do
foo=bar
echo foo1: $foo
done;}
echo foo2: $foo
# Example output:
# foo1: bar
# foo2: Advertisement
<google uid="C01"></google>
Comments
{{#widget:DISQUS |id=fvue |uniqid=Bash: Piped `while-read' loop starts subshell |url=https://fvue.nl/wiki/Bash:_Piped_%60while-read%27_loop_starts_subshell }}