xea*_*its 22 shell pipe text-processing buffer stdout
我不明白管道中的数据是如何流动的,希望有人能澄清那里发生了什么。
我认为命令管道以逐行方式处理文件(文本、字符串数组)。(如果每个命令本身逐行工作。)每行文本都通过管道,命令不会等待前一个完成处理整个输入。
但似乎并非如此。
这是一个测试示例。有几行文字。我将它们大写并重复每行两次。我这样做cat text | tr '[:lower:]' '[:upper:]' | sed 'p'。
为了遵循这个过程,我们可以“交互地”运行它——跳过cat. 管道的每个部分逐行运行:
$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2
Run Code Online (Sandbox Code Playgroud)
但是完整的管道确实等待我完成输入,EOF然后才打印结果:
$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D
Run Code Online (Sandbox Code Playgroud)
应该是这样吗?为什么不是一行一行的?
小智 37
stdio大多数 unix 程序使用的 C 标准 I/O 库 ( )后面都有一个通用缓冲规则。如果输出到终端,则在每行末尾刷新;否则,仅当缓冲区(在我的 Linux/amd64 系统上为 8K;在您的系统上可能不同)已满时才会刷新它。
如果你所有的事业都按照一般规则,你会看到在所有的例子输出延迟(cat|sed,cat|tr,和cat|tr|sed)。但有一个例外:GNUcat从不缓冲其输出。它要么不使用,要么stdio更改默认stdio缓冲策略。
我可以相当确定您使用的是 GNUcat而不是其他一些 unix,cat因为其他人不会这样做。传统的 unixcat有一个-u选项来请求无缓冲输出。GNUcat忽略该-u选项,因为它的输出总是无缓冲的。
所以每当你有一个cat左边有a的管道时,在GNU系统中,数据通过管道的传递不会被延迟。该cat甚至不打算一行行-你的终端做。当您为 cat 输入输入时,您的终端处于“规范”模式 - 基于行,使用退格键和 ctrl-U 等编辑键让您有机会在使用Enter.
在这个cat|tr|sed例子中,只要你按下,tr它仍然会从那里接收数据,但遵循默认策略:它的输出将进入一个管道,所以它不会在每一行之后刷新。当缓冲区已满或收到 EOF 时,它会写入第二个管道,以先到者为准。catEntertrstdio
sed也遵循stdio默认策略,但它的输出将发送到终端,因此它会在完成后立即写入每一行。这会影响在管道另一端出现某些内容之前必须键入的内容 - 如果sed正在块缓冲其输出,则您必须键入两倍的内容(以填充tr的输出缓冲区和 sed的输出缓冲)。
GNUsed有-u选项,所以如果你颠倒顺序并使用cat|sed -u|tr你会看到输出立即再次出现。(该sed -u选项可能在其他地方可用,但我不认为这是一个古老的 unix 传统cat -u)据我所知,没有等效的tr.
有一个名为的实用程序stdbuf,可让您更改使用stdio默认值的任何命令的缓冲模式。它有点脆弱,因为它LD_PRELOAD用来完成 C 库不支持的东西,但在这种情况下它似乎工作:
cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'
Run Code Online (Sandbox Code Playgroud)
小智 9
这实际上让我花了一些时间来理解,甚至更多地回答。很好的问题(我接下来会投票)。
您忽略了tr | sed在上面的调试项目中尝试:
>tr '[:lower:]' '[:upper:]' | sed 'p'
i am writing
still writing
now ctrl-d
I AM WRITING
I AM WRITING
STILL WRITING
STILL WRITING
NOW CTRL-D
NOW CTRL-D
>
Run Code Online (Sandbox Code Playgroud)
所以显然是tr缓冲。每天学些新东西!
编辑:
当我考虑到这一点时,我们已经隔离了原因,但没有提供解释。如果cat | tr,它写入向右走,如果你cat | sed,它写入向右走,但如果你tr | sed,它等待了EOF。我建议答案可能隐藏在tr或sed源代码中,而不是管道问题。
编辑:
我看到 Wumpus在我输入最后一次编辑时提供了解释。谢谢!