Reputation: 11720
Consider the following script:
#!/bin/bash
num=0
cat file | while read line; do
echo "$line"
lines[$num]="$line"
((num++))
echo "num = $num"
done
echo "end num = $num"
i=0
while [ $i -lt $num ]; do
echo "${lines[$i]}"
((i++))
done
Normally, it should read the file line by line, store the result in an array, then go through the array and print it line by line. The problem is that the variable $num resets somehow after the first loop exits. The output of this script for me is the following (using a file with some random garbage in it):
dsfkljhhsdfsdfshdjkfgd
num = 1
fdfgdfgdfg
num = 2
dfgdfgdfgdfg
num = 3
dfgdfgdfgdfgdfg
num = 4
dfgdfgdfgdfgdfgd
num = 5
fgdfgdfgdfg
num = 6
dfgdfgdfgdfg
num = 7
dfgdfgdfgdfgdfg
num = 8
dfgdfgdfgdfg
num = 9
dfgdfgdgdgdg
num = 10
dfgdffgdgdgdg
num = 11
end num = 0
Why is this? How do I achieve to remember the variable? I am using bash 3.1.17 on SUSE Linux 10.
Upvotes: 9
Views: 17939
Reputation: 240
Simply keep your variables all in the same environment. On line 4, remove "cat file | ". On line 9, append " < file". Your end num will now contain the expected 11, and the lines array will print one element at a time via your second loop.
Upvotes: 0
Reputation: 881153
Why? It's because this:
cat file | while read line; do
echo "$line"
lines[$num]="$line"
((num++))
echo "num = $num"
done
runs the while
statement in a separate process, with its own environment, not touching the parent environment. You'll find, similarly, that the lines
array is not there either.
The following simplified script shows this in action:
#!/bin/bash
export xyzzy=42
echo urk | while read line; do
xyzzy=999
echo $xyzzy
done
echo $xyzzy
The output of that script is:
999
42
because the setting of the variable to 999
is done in the subprocess.
Bottom line, if you want information to be reflected in the current process (the script), you'll need to do the work in the script or find some other way to get the information out of the sub-process.
If you use input redirection rather than starting a sub-process pipeline, it should work as you want. That's because the while
bit is then done in the context of the current process rather than a separate process in a pipeline. For example:
#!/bin/bash
export xyzzy=42
while read line; do
xyzzy=999
echo $xyzzy
done <<EOF
hello
EOF
echo $xyzzy
will produce:
999
999
For your specific case, replace:
done <<EOF
hello
EOF
with:
done <file
Upvotes: 15
Reputation: 27990
To add to the previous answer: and to avoid that (and the UUOC), you'll need something like this:
while ...; do
...
done < file
Upvotes: 3