我习惯了while 循环中的bash
内置函数,例如:read
echo "0 1
1 1
1 2
2 3" |\
while read A B; do
echo $A + $B | bc;
done
我一直在从事一些make
项目,分割文件和存储中间结果变得谨慎。因此,我经常最终将单行分解为变量。虽然下面的例子效果很好,
head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done
这有点愚蠢,因为 while 循环永远不会运行多次。但如果没有while
,
head -n1 somefile | read A B C D E FOO; [... use vars here ...]
当我使用它们时,读取的变量总是空的。我从未注意到 的这种行为read
,因为通常我会使用 while 循环来处理许多类似的行。如何在没有 while 循环的情况下使用bash
's内置函数?read
或者是否有另一种(甚至更好)的方法将一行读入多个(!)变量?
结论
答案告诉我们,这是一个范围界定的问题。该声明
cmd0; cmd1; cmd2 | cmd3; cmd4
被解释为命令cmd0
、cmd1
和cmd4
在相同的作用域中执行,而命令cmd2
和cmd3
每个都被赋予自己的子 shell,因此具有不同的作用域。原始 shell 是两个子 shell 的父 shell。
答案1
这是因为使用变量的部分是一组新的命令。使用这个代替:
head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }
请注意,在此语法中,在 后面必须有一个空格,在 之前必须{
有一个(分号) 。也没有必要;只读取第一行。;
}
-n1
read
为了更好地理解,这可能会对您有所帮助;它的作用与上面相同:
read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO
编辑:
人们常说接下来的两个语句具有相同的作用:
head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)
嗯,不完全是。第一个是 from head
tobash
的内置管道read
。一个进程的标准输出到另一个进程的标准输入。
第二个语句是重定向和进程替换。它是由bash
它自己处理的。它创建一个 FIFO(命名管道<(...)
) ,head
其输出连接到该 FIFO,并将<
其重定向 () 到read
进程。
到目前为止,这些看起来是等价的。但当使用变量时,这可能很重要。在第一个中,执行后未设置变量。在第二个中,它们在当前环境中可用。
在这种情况下,每个 shell 都有不同的行为。看那个链接他们是为了这个。您可以使用命令分组、进程替换 ( ) 或此处字符串 ( )bash
来解决该行为。{}
< <()
<<<
答案2
引用一篇非常有用的文章wiki.bash-hackers.org:
这是因为管道的命令在无法修改父 shell 的子 shell 中运行。因此,父 shell 的变量不会被修改(参见文章:Bash 和进程树)。
由于答案已经提供了几次,另一种方法(使用非内置命令......)是这样的:
$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0
答案3
正如您所指出的,问题在于管道read
在子 shell 中运行。
一个答案是使用特雷多克:
numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second
这个方法很好,因为它在任何类似 POSIX 的 shell 中都会以相同的方式运行。