读循环时的问题

时间:2015-03-09 07:29:34

标签: bash

关于while循环工作的一件事。

我有以下代码:

f2(){ if [[ -t 0 ]] ; then echo "$*" ; else echo $(cat) ; fi ; }
echo -e 'a\nb\nc' | while read var ; do f2 $var ; done

对我来说有趣的是为什么'a'字母不打印。 但是,如果我使用for循环,一切似乎都按预期工作。 像这样:

for var in `echo -e 'a\nb\nc'`; do f2 $var ; done

它保留了这些回声,因为它们是在stdout上提供输出的其他脚本的替代品,但是它们给出了完全相同的结果。缺少一些应该打印的值。

提前谢谢大家。

2 个答案:

答案 0 :(得分:3)

让我们仔细看看这一行:

echo -e 'a\nb\nc' | while read var ; do f2 $var ; done

这:

  • 创建管道
  • 启动子进程以运行echo命令,将其stdout重定向到管道
  • 启动子进程以运行while命令,将其stdin重定向到管道。

因此,让我们使用这个有用的工具将其简化为基本要素:

readlink -f /dev/stdin

readlink -f /dev/stdin(无论如何,在Linux上)会告诉我们stdin解决了什么问题。在交互式shell中,它将显示stdin是终端:

$ readlink -f /dev/stdin
/dev/pts/14

现在,让我们回到管道:

$ echo | readlink -f /dev/stdin
/proc/28050/fd/pipe:[608866]

正如预期的那样,stdin是一个管道。

现在,管道的实际右侧是:

| while read var ; do f2 $var ; done

其中f2首先检查if [[ -t 0 ]]:换句话说,f2会检查stdin是否为终端。我们可以看到stdin 不是终端,因此f2将继续else子句:

echo $(cat)

打印剩余的stdin,用空格替换行尾。 stdin重定向最初是三行,abc,但read已经读取了第一行,所以剩下的就是{ {1}}和b,这就是c将返回的内容。

毋庸置疑,逻辑是错误的。为什么要cat检查其输入是否已被重定向?

更正常的可能是检查是否有任何参数:f2。但由于我不知道你想做什么,我无法提供明确的解决方案。

虽然在这种情况下修复if (($#)); then可能更好,但可以通过使用进程替换和不同的文件描述符来避免对f2循环使用stdin:< / p>

while read

此处,while read -u3 var ; do f2 $var ; done 3< <(echo $'a\nb\nc') 导致文件描述符3(未使用的文件描述符)的重定向,3<告诉read -u3从fd 3而不是read读取。 stdinprocess substitution,它创建了一个名称,可用于读取命令的输出。

答案 1 :(得分:0)

我相信'a'会进入var,其余的stdin会被cat消耗/处理。

以下示例可能有助于可视化:

在此示例中,只有'read'使用stdin。

echo -e 'a\nb\nc' | while read var ; do echo Iterated $var ; done
Iterated a
Iterated b
Iterated c

使用cat,read会占用stdin的第一部分,其余部分会被cat消耗掉。

$ echo -e 'a\nb\nc' | while read var ; do echo $(cat); echo Iterated $var ; done
b c
Iterated a