在Linux中,是否可以运行管道:
cmd1 | cmd2
Run Code Online (Sandbox Code Playgroud)
以这样的方式:
cmd2cmd1直到完全完成才开始运行,并且
如果cmd1有错误,则cmd2根本不运行,并且管道的退出状态是 的退出状态cmd1。
举个例子,如何制作这个管道:
false | echo ok
Run Code Online (Sandbox Code Playgroud)
什么都不输出并返回非零状态?
set -o pipefail
Run Code Online (Sandbox Code Playgroud)
该管道确实具有非零退出状态,但cmd2即使cmd1失败仍然运行。
cmd1 && cmd2
Run Code Online (Sandbox Code Playgroud)
这不是管道。无 I/O 重定向。
mkfifo /tmp/fifo
cmd1 > /tmp/fifo && cmd2 < /tmp/fifo
Run Code Online (Sandbox Code Playgroud)
它会阻塞。
touch /tmp/file
cmd1 > /tmp/file && cmd2 < /tmp/file
Run Code Online (Sandbox Code Playgroud)
这似乎有效。但它有几个缺点:
它将数据写入 I/O 速度较慢的磁盘。(当然您可以使用tmpfs,但这是一个额外的系统要求)。
您必须仔细选择临时文件名。否则它可能会意外覆盖现有文件。mktemp可能会有所帮助,但未命名管道可以完全节省您的命名工作。
临时文件所在的文件系统可能不够大,无法容纳整个数据。
临时文件不会自动清理。
我们不知道cmd1输出的大小,但管道的缓冲区大小有限。一旦将一定量的数据写入管道,任何后续写入都将阻塞,直到有人读取管道(类似于失败的解决方案 3)。
您必须使用保证不阻塞的机制。对于非常大的数据,请使用临时文件。否则,如果您有能力将数据保留在内存中(这毕竟是管道的想法),请使用:
\n\nresult=$(cmd1) && cmd2 < <(printf \'%s\' "$result")\nunset result\nRun Code Online (Sandbox Code Playgroud)\n\n这里 的结果cmd1存储在变量 中result。如果cmd1成功,cmd2则执行并提供 中的数据result。最后,result取消设置以释放相关内存。
注意:以前,我使用此处字符串 ( <<< "$result") 来提供cmd2数据,但 St\xc3\xa9phane Chazelas 观察到这bash会创建一个您不想要的临时文件。
回答评论中的问题:
\n\n是的,命令可以随意链接:
\n\nresult=$(cmd1) \\\n&& result=$(cmd2 < <(printf \'%s\' "$result")) \\\n&& result=$(cmd3 < <(printf \'%s\' "$result")) \\\n...\n&& cmdN < <(printf \'%s\' "$result")\nunset result\nRun Code Online (Sandbox Code Playgroud)不,上述解决方案不适合二进制数据,因为:
\n\n$(...)会吃掉所有尾随换行符。\\0命令替换结果中的NUL 字符 ( ) 的行为未指定(例如,Bash 会丢弃它们)。是的,为了避免二进制数据的所有这些问题,您可以使用类似的编码器base64(或uuencode,或自制的编码器,仅处理 NUL 字符和尾随换行符):
result=$(cmd1 > >(base64)) && cmd2 < <(printf \'%s\' "$result" | base64 -d)\nunset result\nRun Code Online (Sandbox Code Playgroud)\n\n在这里,我必须使用进程替换 ( >(...)) 才能保持cmd1退出值不变。
也就是说,仅仅为了确保数据不写入磁盘似乎就相当麻烦。中间临时文件是更好的解决方案。请参阅St\xc3\xa9phane\ 的答案,它解决了您对此的大部分担忧。
\n