mkfifo foo
printf %s\\n bar | tee foo &
tr -s '[:lower:]' '[:upper:]' <foo
wait
rm foo
这是我想要执行的操作的有效 POSIX shell 脚本:
printf %s\\n bar
是产生标准输出的外部程序的象征tr -s '[:lower:]' '[:upper:]'
象征着另一个应该接收标准输出并对其执行某些操作的命令tee
将 stdout 复制到命名管道 foo
输出符合预期:
bar
BAR
现在我想整理一下代码,让它变成这样external_program | my_function
。就像这样:
f() (
mkfifo foo
tee foo &
tr -s '[:lower:]' '[:upper:]' <foo
wait
rm foo
)
printf %s\\n bar | f
但现在根本就没有任何输出。
问题的核心是 POSIX 有这样的(通常无用的)要求,即
sh
异步命令必须将其标准输入重定向到,/dev/null
除非有明确的标准输入重定向(嗯,从技术上讲,如果有的话,隐式/dev/null
重定向发生在显式重定向之前)。例如,参见基于 Linux 的系统:
使异步命令的标准输入保持不变的常见解决方法是执行以下操作:
其中,除了命令组中的 0 之外,原始 stdin 还可在 fd 3 上使用,并且在由于 而重新打开的
3<&0
命令组cmd
stdin 中,正在通过该 fd 3 重定向回原始 stdin(我们随后关闭它,因为不再需要它)。/dev/null
&
在:
tee
的标准输入将是/dev/null
,而不是 管道的读取端printf
。将其更改为:会解决这个问题,但正如你所发现的,
由于 then
tee
不是异步运行的,所以没有将其标准输入重定向到/dev/null
,并且tr
的标准输入被明确重定向,因此事先将其重定向到 并不重要/dev/null
。我们也不需要
wait
,因为tee
该进程是同步运行的(在 shell 隐式等待之后)并且通常不会在tr
它等待其 stdin 上的 eof 之前终止(并且tr
的 stdout 是该管道的另一端仅在退出时关闭)。您可能仍想等待
tr
以检索其退出状态:foo
如果函数主体的子 shell 被终止,则不会被删除。您可以减少 fifo 存在的窗口,并使其更像一个未命名的管道(其中命名管道只是这两个进程建立管道的短暂会面地点),方法是在两个进程以读写模式打开它后立即将其删除。正如所怀疑的那样,问题似乎正在尝试通过管道传输
external_program
到分离的tee
。最初的剧本里有:
它不
tee
只是分离,而是将整个管道序列合并在一起。所以我最初做的是这样的管道,它会立即得到解决,即 bothsleep
和 noop:
一起分离:但是,在函数内部分离时
tee
,只有tee
被分离,而不是管道开头的外部程序。重新使用 的示例sleep
,我们可以看到只有 noop:
被分离,但sleep
没有,并且脚本需要整整 10 秒:解决方案不是使用 detach
tee
,而是使用其他命令:现在
tee
可以从管道接收stdout,写入FIFO,函数的输出再次是: