7

我可以让它在 ksh 中工作,但不能在 bash 中工作,这真的让我发疯。希望这是我忽略的显而易见的事情。

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

这个简化的示例看起来像是在循环中正确设置了数组,但是在循环完成后,这些数组分配消失了吗?就好像循环被完全视为一个外壳?

垃圾.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

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

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

4

1 回答 1

9

因为您的 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 或更高版本,则可以在循环之前执行这两个命令,原始管道进入循环将起作用,因为 while 循环是管道中的最后一个命令。

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) )

您的命令的输出仅根据换行符拆分为单词(因此每一行都是一个单独的单词),并将生成的 line-per-slot 数组附加到原始数组。如果您只使用循环来构建数组,这非常好。它也可以被修改以适应少量的每行处理,类似于 Python 列表理解。

于 2012-08-13T21:02:48.897 回答