让我首先介绍我的发现,然后再提出我的问题。 (1)仅适用于zsh
,(2),(3)适用于zsh
和bash
。
ls | echo $(cat)
ls | { echo $(cat) }
第一个打印 cat: -: Input/output error
;而第二个产生ls
的输出。
ls | { head -n1; cat}
ls | { read a; cat}
第一个命令不能正常工作。 cat
遇到EOF
并直接退出。但是第二种形式起作用:第一行被读入a
,cat
得到其余的内容。
ls | { python -c 'import sys; print(sys.argv)' $(head -n1) }
ls | { python -c 'import sys; print(sys.argv); print(input())' $(head -n1) }
在第一行的{}
内,命令是打印cmdline参数。在第二种形式中,该命令还会从stdin
中读取一行。
由于input()
读取EOF
导致第二个表单抛出错误,因此第一个命令可以成功运行。
{}
和不带head
的表单有什么区别? cat
和stdin
是否可以顺序读取相同的stdin
?在第一种形式失败的同时,第二种形式又如何成功?echo
连接到原始命令的标准输入(此处为stdin
)。谁先读?以及如何使python
保持打开状态,以便两个命令(head
和stdin
)可以依次读取相同的UserDetails : id,UserName,IsShow
Brand : id,BrandName
Product : id,ProductName
BrandProduct:id,brandid,productid,price
?答案 0 :(得分:2)
您没有考虑输入缓冲,它解释了您的大多数观察结果。
head
每次需要数据时都读取几千字节的输入,这使其效率更高。因此,很可能它将在任何其他进程有机会读取之前的所有stdin。在情况2中,这很明显,执行顺序可能更清晰。
如果输入来自常规文件,则head
可以返回到终止之前使用的行的末尾。但是由于管道是不可寻找的,所以它不能这样做。如果您使用“ here-strings”(<<<
语法),则stdin将变得可搜索,因为here-strings是使用临时文件实现的。不过,我不知道您是否可以依靠这一事实。
read
不会缓冲输入,至少不会超出当前行(即使只有在命令行上未指定其他行尾定界符的情况下,也是如此)。它只仔细读取所需的内容,因为它通常用于其输入来自管道且无法进行查找的上下文中。这是非常有用的-如此之多以至于它几乎是不可见的-但这也是shell脚本编写缓慢的原因之一。
通过将足够的数据发送到管道中以满足head
的初始读取,您可以更清楚地看到这一点。试试这个,例如:
seq 1 10000 | { head -n1; head -n2; }
(我将第二个head
更改为head -n2
,因为第一个head
恰好将stdin
留在了行尾,因此第二个{{ 1}}将空白行作为第一行。)
您需要了解的另一件事是命令替换的作用以及何时执行。命令替换读取命令的整个输出,并将其插入命令行。这种情况甚至在命令被识别之前就已发生,不必介意开始执行。
考虑以下小片段:
head
应该清楚的是,在启动echo实用程序(或内置程序)之前,已完全执行命令替换。
您的第一个场景触发$(printf %cc%co e h) hello, world
的奇数,这由StéphaneChazelas在this answer on Unix.SE中解释。实际上,zsh
在建立管道之前先执行命令替换 ,因此zsh
从主要zsh的标准输入中读取。 (Stéphane解释了为什么会这样,以及它如何导致EIO错误。尽管我认为这取决于精确的zsh配置和选项设置,但是由于默认安装了zsh,它只会锁定我的终端。在某个时候,我会锁定必须弄清楚原因。)如果使用花括号,则将在执行命令替换之前设置重定向。