Bash 将 stdio 重定向到命名管道

Posted

技术标签:

【中文标题】Bash 将 stdio 重定向到命名管道【英文标题】:Bash redirecting stdio to named pipes 【发布时间】:2014-09-08 14:38:50 【问题描述】:

我有几个异步运行的进程,每个进程都从一个 fifo 获取输入并将其输出到另一个 fifo。我想异步阅读这些。问题是我似乎无法在 fifo 关闭之前获得输出。这是一个例子

#!/bin/bash -x

rm -rf /tmp/fifo[123].in,out
for i in $(seq 1 3) ; do
    mkfifo /tmp/fifo$i.in
    mkfifo /tmp/fifo$i.out
done

fin1=/tmp/fifo1.in
fout1=/tmp/fifo1.out

fin2=/tmp/fifo2.in
fout2=/tmp/fifo2.out

fin3=/tmp/fifo3.in
fout3=/tmp/fifo3.out

(echo "Proc 1"; while read var; do echo "proc1: " $var; done) < $fin1 > $fout1 &
(echo "Proc 2"; while read var; do echo "proc2: " $var; done) < $fin2 > $fout2 &
(echo "Proc 3"; while read var; do echo "proc3: " $var; done) < $fin3 > $fout3 &

# Get some of the output
cat $fout1 >> /tmp/test.before
cat $fout2 >> /tmp/test.before
cat $fout3 >> /tmp/test.before

# Generate more output
echo "Do you copy proc 1" > $fin1
echo "Do you copy proc 1" > $fin2
echo "Do you copy proc 1" > $fin3

# Get the rest of the output
cat $fout1 >> /tmp/test.after
cat $fout2 >> /tmp/test.after
cat $fout3 >> /tmp/test.after

# Show the results
echo "Before input:"
cat /tmp/test.before

echo "After input:"
cat /tmp/test.after

结果

$ ./test.sh
+ rm -rf '/tmp/fifo[123].in' '/tmp/fifo[123].out'
++ seq 1 3
+ for i in '$(seq 1 3)'
+ mkfifo /tmp/fifo1.in
+ mkfifo /tmp/fifo1.out
+ for i in '$(seq 1 3)'
+ mkfifo /tmp/fifo2.in
+ mkfifo /tmp/fifo2.out
+ for i in '$(seq 1 3)'
+ mkfifo /tmp/fifo3.in
+ mkfifo /tmp/fifo3.out
+ fin1=/tmp/fifo1.in
+ fout1=/tmp/fifo1.out
+ fin2=/tmp/fifo2.in
+ fout2=/tmp/fifo2.out
+ fin3=/tmp/fifo3.in
+ fout3=/tmp/fifo3.out
+ cat /tmp/fifo1.out

我怎样才能获得这样的异步通信?我想避免使用协同进程,因为我事先不知道我需要多少异步进程并且事情会变得一团糟。

【问题讨论】:

对于初学者来说,在调用cat $fout1 之前,您似乎没有向$fin1 写入任何内容,因此您的脚本会在那里等待任何内容出现在$fout1 中。 是的,这就是重点,我想在写入$fin之前读取字符串“Proc1” @fakedrake,为什么您希望后台循环在您的主进程到达从$fout 文件读取的点之前完成写入?您正在后台处理整个子外壳,而不仅仅是读取循环,因此第一个 echo 也在异步运行。如果你想要任何时间保证,你需要自己做一些锁定来提供它们。 也就是说——如果你想让它的效率更高,你最好用一种可以访问select()调用的语言或其更新的等价物来实现它。当然,您没有使用coproc 关键字,但您基本上正在按原样使用协进程。 嗯,我的具体情况是我有许多实例 netcat 在几个端口上侦听。当有人向端口发送请求时,我想向 netcat 的标准输入抛出一个针对每个实例不同的特定 sring。有没有办法在 bash 中做到这一点? 【参考方案1】:

添加以下行

for x in $fout1 $fout2 $fout3; do printf "" > $x & done

在您启动三个子进程之前似乎立即打破了僵局。我不完全确定为什么。用一些文本替换空字符串会在test.before 中生成该文本,所以我怀疑整个子shell,而不仅仅是封闭的read,阻塞了I/O 重定向。

【讨论】:

这很有趣,但还不够。它不会阻塞,但我需要 .before 文件中输出的第一部分。【参考方案2】:

我想知道你为什么把 stdout 和 stderr 重定向放在 subshel​​l 之外:

(echo "Proc 1"; while read var; do echo "proc1: " $var; done) < $fin1 > $fout1 &
(echo "Proc 2"; while read var; do echo "proc2: " $var; done) < $fin2 > $fout2 &
(echo "Proc 3"; while read var; do echo "proc3: " $var; done) < $fin3 > $fout3 &

恕我直言,应该变成:

(echo "Proc 1" > $fout1; while read var; do echo "proc1: " $var; done < $fin1 >> $fout1) &
(echo "Proc 2" > $fout2; while read var; do echo "proc2: " $var; done < $fin2 >> $fout2) &
(echo "Proc 3" > $fout3; while read var; do echo "proc3: " $var; done < $fin3 >> $fout3) &
                                                                           look here ^^^^^

所以所有的进程,包括重定向,都在一个从主进程派生的子shell中,主脚本不会在第一个while循环中阻塞。

【讨论】:

在原文中,echo 的输出去了$fout1 等人。也是。在子shell 中移动重定向需要添加到echo 的重定向。

以上是关于Bash 将 stdio 重定向到命名管道的主要内容,如果未能解决你的问题,请参考以下文章

将命名管道输入重定向到文件

无法将多个 shell 语句重定向到命名管道

第4天文件管理管道用户及组管理用户及权限管理

键盘输入错误地重定向到命名管道读取

将子进程的 stdout 和 stderr 重定向到两个命名管道(然后从它们读回)

从命名管道到提交日志的 Julia 输出