Bash: how to update an associative array in an implicit subshell?

Problem: I cannot update the array in a while . Illustration (not a real 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 channel runs in a subshell. All changes that the loop makes to the wordcounts variable are visible only inside the loop.

Saying export wordcounts does not help.

Alas, I seem to need the pipe and the while read part, so the ways to rewrite the code above with for not what I'm looking for.

Is there a legitimate way to update the shape of an associative array in a loop or even in a subshell?

+6
source share
4 answers

Since you have a complex command channel from which you are reading, you can use the following:

 while read f; do # Do stuff done < <(my | complex | command | pipe) 

The <(command) syntax starts a command in a subshell and opens its stdout as a temporary file. You can use it anywhere you usually use a file in a command.

Alternatively, you can also use the syntax >(command) to open stdin as a file.

+7
source

Is there a legitimate way to update the shape of an associative array within a loop or subshell in general?

You can escape the subshell by saying:

 while read f; do ... done < *.txt 

However, your sample code has problems otherwise. The loop will read the file line by line, so

 wordcounts[$f]=$(wc -w $f) 

doesn't make much sense. You probably wanted to say:

 wordcounts[$f]=$(wc -w <<< $f) 

EDIT:

Alas, it seems to me that you need a pipe ...

Quote from manual :

Each command in the pipeline is executed in its own subshell (see Environment Execution Environment ).

+2
source

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 hooked.

Simple demo:

 $ 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 
+2
source

Why use ls unnecessarily.

Works great:

 declare -a wordcounts for f in *.txt; do wordcounts+=$(wc -w $f) done echo ${wordcounts[@]} 
0
source

Source: https://habr.com/ru/post/955197/


All Articles