如何制作重定向循环

如何制作重定向循环

这主要是出于学术原因。 ;-)

考虑以下 Bash 语句:

foo | bar

这会执行两个进程foobar,使得 的标准输出foo连接到 的标准输入bar。到目前为止,一切都很好。

有没有一种方法可以调用foobar以便我们有相反方向的连接吗?

foo.stdout -> bar.stdin
foo.stdin  <- bar.stdout

据我了解,Bash 调用pipe(2)创建一对相互连接的 FD,然后可以使用它们来替换各自子进程中的stdin/ 。stdout在内核级别,我认为没有理由不能调用pipe(2)两次并安排上面描述的圆形排列。

但我实际上无法弄清楚 shell 语法那。

到目前为止,我能想到的最好的想法是使用命名管道,或者启动一个子 shell 并以某种方式进行一些疯狂的 FD 到 FD 重定向......

答案1

在 bash 中,您可以使用共生(bash 对多个 coproc 的支持很差,但这里你只需要一个):

#!/bin/bash
set -e
coproc { while read -r line; do echo "$BASHPID read: $line";  done; }
i=0; while :; do
    echo "$BASHPID writing>> $i"
    echo $i >&"${COPROC[1]}"
    read -r line <&"${COPROC[0]}"
    echo "$BASHPID coproc produced>> $line"
    i=$((i+1))
done

或者命名管道(这些也可以在简单的 POSIX shell 中工作):

#!/bin/bash
set -e
trap 'rm -rf "$tmpd"' EXIT
tmpd=$(mktemp -d)

mkfifo "$tmpd/p0" "$tmpd/p1"
exec 3<>"$tmpd/p0"
exec 4<>"$tmpd/p1"
rm -rf "$tmpd"

( while read -r line; do echo "$BASHPID read: $line";  done; ) <&3 >&4  &
i=0; while :; do
    echo "$BASHPID writing>> $i" 
    echo $i >&3
    read -r line <&4
    echo "$BASHPID coproc produced>> $line"
    i=$((i+1))
done 

如果您不习惯在 shell 中处理 fd,那么它们可能看起来都很丑陋。

此外,由于管道对调度的影响(写入具有完整管道缓冲区的管道会阻塞您,就像从具有空管道缓冲区的管道中读取数据一样),您可能会因某些读/写模式而陷入死锁。

上述两个示例的输出可能如下所示:

32435 writing>> 0
32435 coproc produced>> 32441 read: 0
32435 writing>> 1
32435 coproc produced>> 32441 read: 1
32435 writing>> 2
32435 coproc produced>> 32441 read: 2
32435 writing>> 3
32435 coproc produced>> 32441 read: 3
32435 writing>> 4
32435 coproc produced>> 32441 read: 4

答案2

我不认为你可以用管道(|)来做到这一点,但是使用异步进程相互写入和读取很容易做到。

这个ksh93脚本(bash进一步向下的版本) 启动两个while循环,在它们之间抛出一个数字,并在每个事务中的数字上加一:

while read data; do
    print $(( data + 1 ))
done |&

print -p 1

while read -p data; do
    print $(( data + 1 ))
done >&p
  1. 第一个循环作为协进程启动。它等待其上的输入read
  2. 该编号1被赋予read协进程以开始整个过程​​。
  3. 第二个循环开始执行与第一个循环相同的操作,但不是作为协同进程。

在启用的情况下运行它xtrace(并使用修改后的脚本将PS4跟踪提示设置"& "为第一个循环,将"> "第二个循环设置为,只是为了显示是什么):

$ ksh -x script.sh
& PS4='& '
> PS4='> '
> print -p 1
> 1>& p
> read -p data
& read data
& print 2
& read data
> print 3
> read -p data
& print 4
> print 5
> read -p data
& read data
& print 6
& read data
> print 7
> read -p data
(etc.)

bash也进行协同处理(寻找coproc内置),但我对这些不太熟悉。您也可以在 shell 中执行此操作,而无需协同处理。

编辑:类似这样的事情bash

coproc while read data; do
    echo $(( data + 1 ))
done

echo 1 >&${COPROC[1]}

while read -u ${COPROC[0]} data; do
    echo $(( data + 1 ))
done >&${COPROC[1]}

运行它(使用如上所述修改的跟踪提示):

$ bash -x script.sh
+ PS4='& '
& PS4='> '
> echo 1
> read -u 63 data
& read data
& echo 2
> echo 3
> read -u 63 data
& read data
& echo 4
> echo 5
> read -u 63 data
& read data
& echo 6
> echo 7
> read -u 63 data
(etc.)

答案3

对于更复杂的文件描述符重定向树,还有管道执行器,这已成为Debian 存储库

确实,这通常是 BASH 和类似 Bourne 的 shell 中语法的限制。

相关内容