关于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上提供输出的其他脚本的替代品,但是它们给出了完全相同的结果。缺少一些应该打印的值。
提前谢谢大家。
答案 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
重定向最初是三行,a
,b
和c
,但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
读取。 stdin
是process 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