三重管道到 STDOUT & tee /dev/null >(wc -l > tmp.txt) 的奇怪结果 & 再次管道嵌入 `cat tmp.txt`

三重管道到 STDOUT & tee /dev/null >(wc -l > tmp.txt) 的奇怪结果 & 再次管道嵌入 `cat tmp.txt`
$ seq 1 12773 | tee /dev/null >(wc -l > tmp.txt) | head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

--> 8473 (1~12773之间随机)

$ cat tmp.txt

--> 8473

$ seq 1 12774 | tee /dev/null >(wc -l > tmp.txt) | head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

-->(空)

$ cat tmp.txt

--> 8844 (1~12773之间随机)

$ seq 1 25011 | tee /dev/null >(wc -l > tmp.txt) | cat | head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

--> 13778 (1~25011之间随机)

$ cat tmp.txt

--> 13778

$ seq 1 25012 | tee /dev/null >(wc -l > tmp.txt) | cat |head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

-->(空)

$ cat tmp.txt

--> 24939 (1~25012之间随机)

$ seq 1 46014 | tee /dev/null >(wc -l > tmp.txt) | cat | cat |head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

--> 34111 (1~46014之间随机)

$ cat tmp.txt

--> 34111 (1~46014之间随机)

$ seq 1 46015 | tee /dev/null >(wc -l > tmp.txt) | cat | cat |head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))|tail -1

-->(空)

$ cat tmp.txt

--> 343 (1~46014之间随机)

作为 '| 的数量cat 后面的“(wc -l > tmp.txt)”增加,您可以使上述命令处理更多的行数。

这是怎么回事?

答案1

作为亚历克斯·P已在评论中解释过,管道中的命令并行运行。你似乎确信事实并非如此;请忘记这个错误观念,只要你不敞开心扉,你就无法理解正在发生的事情。

由于进程是并行运行的,因此操作顺序取决于确切的时间,并且可能无法从一次运行复制到下一次运行。

以第一个示例为例,以下命令并行运行:

  • seq 1 12773
  • tee /dev/null
  • wc -l > tmp.txt(进程替换还会创建管道并并行运行命令)
  • head -$((0x`openssl rand -hex 7` % `cat tmp.txt` + 1))— 这涉及三个不同的命令,并head在两个命令opensslcat退出后启动
  • tail -1

由于wc -l > tmp.txtcat tmp.txt并行运行,因此cat tmp.txt与以下输出相关的运行时间是不可预测的wc

  • 它可能会在执行重定向之前运行tmp.txt,并且要么从上一次运行中获取文件(如果有),要么抱怨该文件不存在。
  • 它可以在执行重定向之后但在wc产生任何输出之前运行;在这种情况下,文件为空,因为重定向会截断文件。
  • 它可以在wc生成输出时运行,并且仅拾取输出的开头。在大多数系统上,wc自动生成输出(因为它很短),因此不会发生这种情况。
  • wc它可以在完成输出后运行。

通过实验,我得到了与您相同的结果(在运行内核 3.16 的 Linux 机器上,该机器在其他情况下大部分处于空闲状态):使用seq 1 12773,获取;cat tmp.txt的输出。wc使用seq 1 12774cat tmp.txt选取一个空文件。那么为什么 12773 和 12774 之间存在差异,但低于该值的结果却相当可靠呢?

$ seq 1 12774 | wc -c
65538

阈值是 65536 字节,该值是管道缓冲区的容量。该head …命令启动速度很慢,因为它必须先运行opensslcat完成。当它启动时,管道中的前一个命令会写入管道缓冲区。当管道缓冲区已满时,前一个命令必须停止。当数字达到 12773 时,管道缓冲区永远不会填满,因此很可能seq之前就完成了运行openssl(它要做的工作少了很多)并且wc有时间写入其输出。但是当数字大于 12774 时,管道缓冲区就会被填满;所以tee被困在写入到 的输出中head …,并且尚未完成将输出写入到wc。同时,cat以空文件运行。

tee当您添加更多管道时,每个管道都有自己的缓冲区,因此在停顿之前有更多空间。

相关内容