使用 bash 的 read 内置命令,无需 while 循环

使用 bash 的 read 内置命令,无需 while 循环

我习惯了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

被解释为命令cmd0cmd1cmd4在相同的作用域中执行,而命令cmd2cmd3每个都被赋予自己的子 shell,因此具有不同的作用域。原始 shell 是两个子 shell 的父 shell。

答案1

这是因为使用变量的部分是一组新的命令。使用这个代替:

head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }

请注意,在此语法中,在 后面必须有一个空格,在 之前必须{ 有一个(分号) 。也没有必要;只读取第一行。;}-n1read

为了更好地理解,这可能会对您有所帮助;它的作用与上面相同:

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 headtobash的内置管道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 中都会以相同的方式运行。

相关内容