bash:如何更新隐式子shell中的关联数组?

时间:2013-10-03 15:48:32

标签: arrays bash loops associative-array subshell

问题:我无法在while循环中更新数组。插图(实际问题):

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'

这不起作用,因为管道在子shell中运行后循环。循环对变量wordcounts所做的所有更改仅在循环内可见。

export wordcounts没有帮助。

唉,我似乎需要管道while read部分,所以使用for重写上面代码的方法并不是我想要的。< / p>

是否有合法的方法来更新循环或子shell中的关联数组表单?

5 个答案:

答案 0 :(得分:7)

由于您正在阅读复杂的命令管道,因此可以使用以下命令:

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

语法<(command)在子shell中运行命令并将其stdout作为临时文件打开。您可以在任何通常在命令中使用文件的地方使用它。

此外,您还可以使用语法>(command)将stdin作为文件打开。

答案 1 :(得分:2)

  

是否有合法的方法来更新a中的关联数组表单   循环,或一般的子shell?

你可以通过说:

来避免子shell
while read f; do
  ...
done < *.txt

那就是说,示例代码有问题。循环将逐行读取文件,所以说

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

真的没有多大意义。你可能想说:

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

编辑:

  唉,我好像需要管道......

引用manual

  

管道中的每个命令都在其自己的子shell中执行(参见Command Execution Environment)。

答案 2 :(得分:2)

如果您使用的是bash 4.2,则可以设置lastpipe shell选项以允许while循环(作为管道中的 last 元素)在当前shell而不是子shell。

一个简单的演示:

$ 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

答案 3 :(得分:0)

为什么不必要地使用ls

以下工作正常:

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

答案 4 :(得分:0)

我认为最好的解决方案是Cookyt的解决方案:

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

对我来说,这是行不通的,因为在我的环境中我没有安装/proc,因为<(cmd)构造需要/dev/fd/XXX并且/dev/fd是指向/proc/self/fd。在这种情况下,chepner的解决方案起作用:

shopt -s lastpipe
my | complex | command | pipe | while read f; do
    # Do stuff
done

如果您还没有bash,那么还有第三种解决方案可用于POSIX shell(因此也适用于bash):

set -- $(my | complex | command | pipe)
while [ -n "$1" ]; do
    f="$1"
    shift
    # Do stuff
done