bash read-while loop in a pipe loses variables in CI
A pipeline runs each stage in its own subshell. So cmd | while read line; do count=$((count+1)); done increments count inside a subshell, and after the loop the parent shell still sees the old value. The loop worked; its variable changes just did not survive.
What this error means
A counter, flag, or accumulated value is set inside a while read loop but is empty or zero after the loop. The loop body clearly ran (you can echo inside it), yet nothing persists.
count=0
grep ERROR log.txt | while read -r line; do
count=$((count + 1))
done
echo "$count" # prints 0: the loop ran in a subshellCommon causes
The loop is the last stage of a pipeline
In bash (default), each pipeline command runs in a subshell. Variables assigned in the loop live and die in that subshell.
Relying on side effects across the pipe boundary
Any state the loop mutates - counters, arrays, flags - is discarded when the subshell exits.
How to fix it
Feed the loop with a redirect or process substitution
Avoid the pipe so the loop runs in the current shell. A here-string or process substitution keeps the loop in the parent.
count=0
while read -r line; do
count=$((count + 1))
done < <(grep ERROR log.txt)
echo "$count" # correctOr enable lastpipe in bash
With job control off, shopt -s lastpipe runs the last pipeline stage in the current shell so its variables persist.
shopt -s lastpipe
grep ERROR log.txt | while read -r l; do count=$((count+1)); done
echo "$count"How to prevent it
- Feed
while readfrom< fileor< <(cmd), not a pipe. - Keep any needed state outside the piped subshell.
- Use
shopt -s lastpipewhen a pipe into a loop is unavoidable.