两个异步子 shell 命令可以安全地写入共享的标准输出吗?

Dan*_*iel 5 pipe concurrency

stdout 是否可以被两个异步运行的 bourne(或 bash,如果重要的话)子 shell 命令覆盖?

\n\n
(tail -f ./file1 & tail -f ./file2) | cat\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不关心行顺序,只是每个输出行都由一个输入行组成。I\xe2\x80\x99m 担心某些行可能会被部分覆盖或交错。

\n\n

我\xe2\x80\x99 通过运行四个命令进行了测试,每个命令输出一个唯一的行 1500 万次。它似乎有效,但我有点预计它会失败。

\n\n

有人可以解释一下这如何不会破坏\xe2\x80\x99t吗?每个子 shell 是否都被缓冲并且一次只有一个子 shell 可以写入标准输出?或者这是如何管理的。

\n\n

有一个更好的方法吗?

\n\n

(不要介意 I\xe2\x80\x99m在上述子 shell 中用于tail说明目的。我实际上想运行另外两个命令,一次连续输出一行到标准输出。)

\n

Sté*_*las 4

贝壳在那里几乎没有参与。他们所做的就是创建管道并启动这 3 个命令,然后这些命令独立于 shell 并行运行。

这里重要的是两个 tail 命令都将文件描述符写入同一管道的同一写入端。

如果你这样做:

printf foo1 >> file1; sleep 1
printf foo2 >> file2; sleep 1
printf 'bar1\n' >> file1; sleep 1
printf 'bar2\n' >> file2
Run Code Online (Sandbox Code Playgroud)

你会看到的:

foo1foo2bar1
bar2
Run Code Online (Sandbox Code Playgroud)

因为那些都是这么写的。您需要确保您的命令一次输出一行,并且这些行小于 PIPE_BUF(Linux 上为 4096 字节),以保证 write() 是原子的(它也可以写入多个一次整行,前提是它们都已满并且它们的累积大小小于 PIPE_BUF)。

使用 GNU grep,您可以通过将命令传递到grep --line-buffered '^'

(tail -f ./file1 | grep --line-buffered '^' &
 tail -f ./file2 | grep --line-buffered '^') | cat
Run Code Online (Sandbox Code Playgroud)

这将保证两个write()命令的输出的每一行都有一个系统调用(在命令不终止其最后一行输出的情况下,grep将添加缺少的换行符)