The problem: I cannot update an array in a while
loop. An illustration (not the actual problem):
declare -A wordcounts
wordcounts["sentinel"]=1000
ls *.txt | while read f; do
# assume that that loop runs multiple times
wordcounts[$f]=$(wc -w $f)
echo ${wordcounts[$f]} # this prints actual data
done
echo ${!wordcounts[@]} # only prints 'sentinel'
This does not work, because the loop after the pipe runs in a subshell. All the changes that the loop does to variable wordcounts
are only visible inside the loop.
Saying export wordcounts
does not help.
Alas, I seem to need the pipe and the while read
part, so ways to rewrite the code above using for
is not what I'm looking for.
Is there a legitimate way to update an associative array form within a loop, or a subshell in general?
Since you have a complex command pipe you're reading from, you can use the following:
while read f; do
# Do stuff
done < <(my | complex | command | pipe)
The syntax <(command)
runs a command in a subshell and opens its stdout as a temporary file. You can use it any place where you would normally use a file in a command.
Further, you can also use the syntax >(command)
to open stdin as a file instead.
If you are using bash
4.2, you can set the lastpipe
shell option to allow the while loop, as the last element in the pipeline, to run in the current shell instead of a subshell.
A simple demonstration:
$ echo foo | read word
$ echo $word
$ set +m # Only needed in an interactive shell to disable job control
$ shopt -s lastpipe
$ echo foo | read word
$ echo $word
foo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With