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 重定向放在 subshell 之外:
(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 重定向到命名管道的主要内容,如果未能解决你的问题,请参考以下文章