在shell / bash中连接两个命令之间的输入_and_output

时间:2021-08-28 15:43:42

I have two (UNIX) programs A and B that read and write from stdin/stdout.

我有两个(UNIX)程序A和B从stdin / stdout读取和写入。

My first problem is how to connect the stdout of A to stdin of B and the stdout of B to the stdin of A. I.e., something like A | B but a bidirectional pipe. I suspect I could solve this by using exec to redirect but I could not get it to work. The programs are interactive so a temporary file would not work.

我的第一个问题是如何将A的stdout连接到B的stdin和B的stdout连接到A.的stdin。即A | B但是双向管道。我怀疑我可以通过使用exec重定向来解决这个问题,但我无法让它工作。程序是交互式的,因此临时文件不起作用。

The second problem is that I would like to duplicate each direction and pipe a duplicate via a logging program to stdout so that I can see the (text-line based) traffic that pass between the programs. Here I may get away with tee >(...) if I can solve the first problem.

第二个问题是我想复制每个方向并通过日志记录程序将副本传递给stdout,以便我可以看到在程序之间传递的(基于文本行的)流量。如果我能解决第一个问题,我可以在这里使用tee>(...)。

Both these problems seems like they should have well known solutions but I have not be able to find anything.

这两个问题似乎都应该是众所周知的解决方案,但我找不到任何东西。

I would prefer a POSIX shell solution, or at least something that works in bash on cygwin.

我更喜欢POSIX shell解决方案,或者至少在cygwin上使用bash工作的东西。

Thanks to your answers I came up with the following solution. The A/B commands uses nc to listen to two ports. The logging program uses sed (with -u for unbuffered processing).

感谢您的回答,我提出了以下解决方案。 A / B命令使用nc来侦听两个端口。日志记录程序使用sed(使用-u进行无缓冲处理)。

bash-3.2$ fifodir=$(mktemp -d)
bash-3.2$ mkfifo "$fifodir/echoAtoB"
bash-3.2$ mkfifo "$fifodir/echoBtoA"
bash-3.2$ sed -u 's/^/A->B: /' "$fifodir/echoAtoB" &
bash-3.2$ sed -u 's/^/B->A: /' "$fifodir/echoBtoA" &
bash-3.2$ mkfifo "$fifodir/loopback"
bash-3.2$ nc -l -p 47002 < "$fifodir/loopback" \
          | tee "$fifodir/echoAtoB" \
          | nc -l -p 47001 \
          | tee "$fifodir/echoBtoA" > "$fifodir/loopback"

This listens for connection to port 47001 and 47002 and echos all traffic to standard output.

这将侦听与端口47001和47002的连接,并将所有流量回显到标准输出。

In shell 2 do:

在shell 2中做:

bash-3.2$ nc localhost 47001

In shell 3 do:

在shell 3中做:

bash-3.2$ nc localhost 47002

Now lines entered in shell 2 will be written to shell 3 and vice versa and the traffic logged to shell 1, something like:

现在在shell 2中输入的行将被写入shell 3,反之亦然,并且流量记录到shell 1,类似于:

B->A: input to port 47001
A->B: input to port 47002

The above has been tested on Cygwin

以上内容已在Cygwin上测试过

Update: The script above stopped working after a few days(!). Apparently it can deadlock. Some of the suggestions in the answers may be more reliable.

更新:上面的脚本在几天后停止工作(!)。显然它可以陷入僵局。答案中的一些建议可能更可靠。

8 个解决方案

#2


10  

How about a named pipe?

命名管道怎么样?

# mkfifo foo
# A < foo | B > foo
# rm foo

For your second part I believe tee is the correct answer. So it becomes:

对于你的第二部分,我相信发球是正确的答案。所以它变成了:

# A < foo | tee logfile | B > foo

#3


5  

You could probably get away with named pipes:

您可能可以使用命名管道:

mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe

#4


4  

You can use Expect.

你可以使用Expect。

Expect is a tool for automating interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, etc.

Expect是一个自动化交互式应用程序的工具,如telnet,f​​tp,passwd,fsck,rlogin,tip等。

You could use the following code (taken from the Exploring Expect book) as a starting point - it connects the output of proc1 to the input of proc2 and vice versa, as you requested:

您可以使用以下代码(取自Exploring Expect书)作为起点 - 它按照您的要求将proc1的输出连接到proc2的输入,反之亦然:

#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1

#5


3  

I spent a lot of time on this, gave it up, and last decided to use ksh (the Korn shell), which allows this.

我花了很多时间在这上面,放弃了,最后决定使用ksh(Korn shell),这允许这样做。

cmd1 |& cmd2 >&p <&p

where |& is a (pipe) operator to start a co-process and &p is file descriptor of that co-process.

其中|&是(管道)运算符以启动协同进程,而&p是该协同进程的文件描述符。

#6


2  

I had this problem at one point, and I threw together this simple C program.

我曾经遇到过这个问题,我将这个简单的C程序汇总在一起。

#include <stdio.h>
#include <unistd.h>

#define PERROR_AND_DIE(_x_) {perror(_x_); _exit(1);}

int main(int argc, char **argv) {
    int fd0[2];
    int fd1[2];


    if ( argc != 3 ) {
        fprintf(stdout, "Usage %s: \"[command 1]\" \"[command 2]\"\n", argv[0]);
        _exit(1);
    }

    if ( pipe(fd0) || pipe(fd1) ) PERROR_AND_DIE("pipe")

    pid_t id = fork();
    if ( id == -1 ) PERROR_AND_DIE("fork");

    if ( id ) {
        if ( -1 == close(0) )  PERROR_AND_DIE("P1: close 0");
        if ( -1 == dup2(fd0[0], 0) ) PERROR_AND_DIE("P1: dup 0"); //Read my STDIN from this pipe

        if ( -1 == close(1) )  PERROR_AND_DIE("P1: close 1");
        if ( -1 == dup2(fd1[1], 1) ) PERROR_AND_DIE("P1: dup 1"); //Write my STDOUT here
        execl("/bin/sh", "/bin/sh", "-c", argv[1], NULL);
        PERROR_AND_DIE("P1: exec")
    }

    if ( -1 == close(0) )  PERROR_AND_DIE("P2: close 0");
    if ( -1 == dup2(fd1[0], 0) ) PERROR_AND_DIE("P2: dup 0");

    if ( -1 == close(1) )  PERROR_AND_DIE("P2: close 1");
    if ( -1 == dup2(fd0[1], 1) ) PERROR_AND_DIE("P2: dup 1");


    execl("/bin/sh", "/bin/sh", "-c", argv[2], NULL);
    PERROR_AND_DIE("P2: exec")
}

#7


0  

This question is similar to one I asked before. The solutions proposed by others were to use named pipes, but I suspect you don't have them in cygwin. Currently I'm sticking to my own (attempt at a) solution, but it requires /dev/fd/0 which you probably also don't have.

这个问题类似于我之前提出的问题。其他人提出的解决方案是使用命名管道,但我怀疑你没有在cygwin中使用它们。目前我坚持自己的(尝试一个)解决方案,但它需要/ dev / fd / 0,你可能也没有。

Although I don't really like the passing-command-lines-as-strings aspect of twinpipe (mentioned by JeeBee (139495)), it might be your only option in cygwin.

虽然我不太喜欢twinpipe的传递 - 命令行 - 字符串方面(由JeeBee(139495)提到),但它可能是你在cygwin中唯一的选择。

#8


0  

I'd suggest "coproc":

我建议“coproc”:

#! /bin/bash
# initiator needs argument

if [ $# -gt 0 ]; then
  a=$1
  echo "Question $a"
else
  read a
fi

if [ $# -gt 0 ]; then
  read a
  echo "$a" >&2
else
  echo "Answer to $a is ..."
fi

exit 0

Then see this session:

然后看这个会话:

$ coproc ./dialog
$ ./dialog  test  < /dev/fd/${COPROC[0]}  > /dev/fd/${COPROC[1]}
Answer to Question test is ...

#1


#2


10  

How about a named pipe?

命名管道怎么样?

# mkfifo foo
# A < foo | B > foo
# rm foo

For your second part I believe tee is the correct answer. So it becomes:

对于你的第二部分,我相信发球是正确的答案。所以它变成了:

# A < foo | tee logfile | B > foo

#3


5  

You could probably get away with named pipes:

您可能可以使用命名管道:

mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe

#4


4  

You can use Expect.

你可以使用Expect。

Expect is a tool for automating interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, etc.

Expect是一个自动化交互式应用程序的工具,如telnet,f​​tp,passwd,fsck,rlogin,tip等。

You could use the following code (taken from the Exploring Expect book) as a starting point - it connects the output of proc1 to the input of proc2 and vice versa, as you requested:

您可以使用以下代码(取自Exploring Expect书)作为起点 - 它按照您的要求将proc1的输出连接到proc2的输入,反之亦然:

#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1

#5


3  

I spent a lot of time on this, gave it up, and last decided to use ksh (the Korn shell), which allows this.

我花了很多时间在这上面,放弃了,最后决定使用ksh(Korn shell),这允许这样做。

cmd1 |& cmd2 >&p <&p

where |& is a (pipe) operator to start a co-process and &p is file descriptor of that co-process.

其中|&是(管道)运算符以启动协同进程,而&p是该协同进程的文件描述符。

#6


2  

I had this problem at one point, and I threw together this simple C program.

我曾经遇到过这个问题,我将这个简单的C程序汇总在一起。

#include <stdio.h>
#include <unistd.h>

#define PERROR_AND_DIE(_x_) {perror(_x_); _exit(1);}

int main(int argc, char **argv) {
    int fd0[2];
    int fd1[2];


    if ( argc != 3 ) {
        fprintf(stdout, "Usage %s: \"[command 1]\" \"[command 2]\"\n", argv[0]);
        _exit(1);
    }

    if ( pipe(fd0) || pipe(fd1) ) PERROR_AND_DIE("pipe")

    pid_t id = fork();
    if ( id == -1 ) PERROR_AND_DIE("fork");

    if ( id ) {
        if ( -1 == close(0) )  PERROR_AND_DIE("P1: close 0");
        if ( -1 == dup2(fd0[0], 0) ) PERROR_AND_DIE("P1: dup 0"); //Read my STDIN from this pipe

        if ( -1 == close(1) )  PERROR_AND_DIE("P1: close 1");
        if ( -1 == dup2(fd1[1], 1) ) PERROR_AND_DIE("P1: dup 1"); //Write my STDOUT here
        execl("/bin/sh", "/bin/sh", "-c", argv[1], NULL);
        PERROR_AND_DIE("P1: exec")
    }

    if ( -1 == close(0) )  PERROR_AND_DIE("P2: close 0");
    if ( -1 == dup2(fd1[0], 0) ) PERROR_AND_DIE("P2: dup 0");

    if ( -1 == close(1) )  PERROR_AND_DIE("P2: close 1");
    if ( -1 == dup2(fd0[1], 1) ) PERROR_AND_DIE("P2: dup 1");


    execl("/bin/sh", "/bin/sh", "-c", argv[2], NULL);
    PERROR_AND_DIE("P2: exec")
}

#7


0  

This question is similar to one I asked before. The solutions proposed by others were to use named pipes, but I suspect you don't have them in cygwin. Currently I'm sticking to my own (attempt at a) solution, but it requires /dev/fd/0 which you probably also don't have.

这个问题类似于我之前提出的问题。其他人提出的解决方案是使用命名管道,但我怀疑你没有在cygwin中使用它们。目前我坚持自己的(尝试一个)解决方案,但它需要/ dev / fd / 0,你可能也没有。

Although I don't really like the passing-command-lines-as-strings aspect of twinpipe (mentioned by JeeBee (139495)), it might be your only option in cygwin.

虽然我不太喜欢twinpipe的传递 - 命令行 - 字符串方面(由JeeBee(139495)提到),但它可能是你在cygwin中唯一的选择。

#8


0  

I'd suggest "coproc":

我建议“coproc”:

#! /bin/bash
# initiator needs argument

if [ $# -gt 0 ]; then
  a=$1
  echo "Question $a"
else
  read a
fi

if [ $# -gt 0 ]; then
  read a
  echo "$a" >&2
else
  echo "Answer to $a is ..."
fi

exit 0

Then see this session:

然后看这个会话:

$ coproc ./dialog
$ ./dialog  test  < /dev/fd/${COPROC[0]}  > /dev/fd/${COPROC[1]}
Answer to Question test is ...