bash在循环中分配数组索引时遇到麻烦

时间:2012-08-13 20:59:06

标签: arrays bash

我可以让这个在ksh中工作但不是在bash中这真的让我疯了。 希望这是我忽视的显而易见的事情。

我需要运行一个外部命令,其中输出的每一行都将存储在数组索引中。

这个简化的例子看起来是正确地在循环中设置数组但是在循环完成之后这些数组赋值消失了?好像循环被完全视为一个外壳?

junk.txt

this is a
test to see
if this works ok

testa.sh

#!/bin/bash

declare -i i=0
declare -a array

echo "Simple Test:"
array[0]="hello"
echo "array[0] = ${array[0]}"

echo -e "\nLoop through junk.txt:"
cat junk.txt | while read line
do
    array[i]="$line"
    echo "array[$i] = ${array[i]}"
    let i++
done

echo -e "\nResults:"
echo "       array[0] = ${array[0]}"
echo " Total in array = ${#array[*]}"
echo "The whole array:"
echo ${array[@]}

输出

Simple Test:
array[0] = hello

Loop through junk.txt:
array[0] = this is a
array[1] = test to see
array[2] = if this works ok

Results:
      array[0] = hello
Total in array = 1
The whole array:
hello

因此,在循环中,我们分配数组[i]并且echo验证它。 但是在循环之后,我回到了包含" hello"的数组[0]。没有其他元素。

bash 3,4和不同平台的结果相同。

1 个答案:

答案 0 :(得分:8)

因为你的while循环在一个管道中,所以循环体中的所有变量赋值都是执行循环的子shell的本地。 (我相信ksh不会在子shell中运行命令,这就是bash中出现问题的原因。)请改为:

while read line
do
    array[i]="$line"
    echo "array[$i] = ${array[i]}"
    let i++
done < junk.txt

很少,如果有的话,您是否希望使用cat单个文件传输到另一个命令;改为使用输入重定向。

更新:由于您需要从命令而不是文件运行,因此另一个选项(如果可用)是进程替换:

while read line; do
...
done < <( command args ... )

如果进程替换不可用,则需要输出到临时文件并从该文件重定向输入。

如果您使用的是bash 4.2或更高版本,则可以在循环之前执行这两个命令,并且原始的pipe-in​​to-the-循环将起作用,因为while循环是 last 命令在管道中。

set +m    # Turn off job control; it's probably already off in a non-interactive script
shopt -s lastpipe
cat junk.txt | while read line; do ...; done

更新2:这是一个基于user1596414评论的无循环解决方案

array[0]=hello
IFS=$'\n' array+=( $(command) )

您的命令输出将分为仅基于换行符的单词(以便每行都是一个单独的单词),并将生成的每插槽行数附加到原始行。如果您只使用循环来构建数组,这非常好。它也可能被修改以适应少量的每行处理,与Python列表理解模糊地类似。