Bash & Shell Scripting DISCUSSION

Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

Started by sarikasourcecode Bash while read loopsubshellpipeline variablesprocess substitutionread -r
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

sarikasourcecode Bash & Shell Scripting Forum
#1

I count error lines like this: count=0; grep ERROR app.log | while read line; do count=$((count+1)); done; echo $count. Inside the loop the counter increases, but the final echo always prints 0.

Why is the variable reset after the loop, and what is the right way to read command output or a file line by line and keep the results?

Community replies 5

Re: Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

#2

Each part of a pipeline runs in its own subshell, a child copy of the shell. The while loop on the right of the | increments its own copy of count, and that copy disappears when the pipeline ends. The parent's variable was never touched, so it still holds 0.

The same thing happens with anything else done in a pipeline segment or inside parentheses: variable assignments, cd and array changes do not survive.

Re: Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

#3

Keep the loop in the main shell and feed it through a redirection instead of a pipe. For a command's output use process substitution: while IFS= read -r line; do count=$((count+1)); done < <(grep ERROR app.log). For a file it is simply done < app.log. Now echo "$count" prints the real total.

Bash 4.2 and later also have shopt -s lastpipe, which runs the last segment of a pipeline in the current shell. It takes effect when job control is off, which is the normal case in scripts but not at an interactive prompt.

Re: Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

#4

Write the read itself as while IFS= read -r line. Without -r, backslashes in the input are treated as escape characters and removed; without the empty IFS=, leading and trailing whitespace is stripped from each line.

If the last line of the file has no terminating newline, read fills the variable but returns a failure status, and the loop skips that line. The usual guard is while IFS= read -r line || [[ -n $line ]]; do.

Re: Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

#5

Often no loop is needed. For this task count=$(grep -c ERROR app.log) gives the number directly. To keep the lines, mapfile -t lines < <(grep ERROR app.log) loads them into an array in the current shell (Bash 4 and later); ${#lines[@]} is then the count and "${lines[0]}" the first match.

Shell loops that start external programs for each line are slow on large files, so where the work is text processing, a single awk or sed call over the whole file is usually the better tool.

Re: Bash variable changed inside a while read loop is empty after the loop: why, and how do I fix it?

#6

A related surprise with these loops: a command inside the body that reads standard input consumes the rest of the file, and the loop ends after one iteration. ssh is the usual culprit. Give that command its own input, ssh -n host uptime or some_command < /dev/null, or read the file on a different descriptor: while IFS= read -r host <&3; do ssh "$host" uptime; done 3< hosts.txt.

TEP COMMUNITY