为什么“echo foo | read a; echo $ a”没有按预期工作?

时间:2008-12-17 14:27:34

标签: linux unix shell

我可以在FreeBSD,GNU / Linux和Solaris下使用各种shell复制问题。它让我头疼了一个多小时,所以我决定在这里发布这个问题。

8 个答案:

答案 0 :(得分:14)

由于管道,read在其自己的子shell中执行。

echo foo | while read a; do echo $a; done

会做你期望的事。

答案 1 :(得分:6)

替代:

echo foo | (read a ; echo $a)

编辑:

如果在子shell外需要$ a,则必须反转命令:

read a < <(echo foo); echo $a

这样,读取在当前进程中执行

答案 2 :(得分:3)

我不认为已经给出了这个:

a=`echo foo`
echo $a

答案 3 :(得分:3)

仅供参考;在ksh它按预期工作;另见http://kornshell.com/doc/faq.html,第III节(shell编程问题),Q13:

Q13.    What is $bar after, echo foo | read bar?
A13.    The is foo.  ksh runs the last component of a pipeline
        in the current process.  Some shells run it as a subshell
        as if you had invoked it as  echo foo | (read bar).

答案 4 :(得分:1)

|是一个进程间通信运算符。因此,隐式地,必须创建一个子流程来解析和评估它的一侧或另一侧的表达式。 Bash和较旧的Bourne shell在运算符右侧创建一个子进程(从管道读取),这意味着设置的任何变量只在范围内,直到该进程退出(在此代码示例中为分号。

zsh和更新版本的Korn shell(至少从'93开始,但可能甚至可以追溯到 ksh '88)将在管道的另一侧创建子进程(写入它)。因此,它们将按照该问题的作者所希望的方式工作。 (“正如预期的那样”当然是非常主观的。理解管道的性质应该导致人们期望这种行为以特定于实现的方式运行。)

我不知道Posix或Spec 1170或SuS中是否有任何特定条款或任何其他需要一个或另一个语义的已发布标准。但是,在实践中,很明显你不能依赖于这种行为;虽然您可以在脚本中轻松测试它。

答案 5 :(得分:0)

我提出了一个解决方案,它不会隐藏子shell中的变量值,并且也可以使用多个值。

set `echo foo bar`
A=$1
B=$2

答案 6 :(得分:-1)

根据Kernighan和Pike的 Unix编程环境(p.159)“没有任何shell内置命令(与控制流原语相反,如同)可以用&gt重定向;和&lt;“,”这可能被描述为shell中的一个bug“。这似乎解释了a)为什么代码像

ls *.c |
while read file
do
   echo $a
done

总是没有问题,b)从文件重定向的不一致性。

答案 7 :(得分:-2)

读取期望在新行上输入

while read a; do echo $a; done
foo
bar
^D

或多或少是你想要的