链接命令和命令替换中的命令的标准输入

时间:2018-10-05 08:20:30

标签: bash zsh

让我首先介绍我的发现,然后再提出我的问题。 (1)仅适用于zsh,(2),(3)适用于zshbash

1。命令替换的标准输入

ls | echo $(cat)
ls | { echo $(cat) }

第一个打印 cat: -: Input/output error ;而第二个产生ls的输出。

2。管道后链接命令

ls | { head -n1; cat}
ls | { read a; cat}

第一个命令不能正常工作。 cat遇到EOF并直接退出。但是第二种形式起作用:第一行被读入acat得到其余的内容。

3。混合标准输入

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导致第二个表单抛出错误,因此第一个命令可以成功运行。

我的问题是:

  1. (如第1节中所述)带{}和不带head的表单有什么区别?
  2. (如第2节所述)catstdin是否可以顺序读取相同的stdin?在第一种形式失败的同时,第二种形式又如何成功?
  3. (如第3节所述)如何将命令替换中的命令echo连接到原始命令的标准输入(此处为stdin)。谁先读?以及如何使python保持打开状态,以便两个命令(headstdin)可以依次读取相同的UserDetails : id,UserName,IsShow Brand : id,BrandName Product : id,ProductName BrandProduct:id,brandid,productid,price

1 个答案:

答案 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,它只会锁定我的终端。在某个时候,我会锁定必须弄清楚原因。)如果使用花括号,则将在执行命令替换之前设置重定向。