【问题标题】:Connecting input _and_output between of two commands in shell/bash在 shell/bash 中连接两个命令之间的输入 _and_output
【发布时间】:2008-09-26 13:24:07
【问题描述】:

我有两个 (UNIX) 程序 A 和 B,它们从标准输入/标准输出读取和写入。

我的第一个问题是如何将 A 的标准输出连接到 B 的标准输入将 B 的标准输出连接到 A 的标准输入。即,类似于 A | B 而是双向管道。我怀疑我可以通过using exec to redirect 解决这个问题,但我无法让它工作。这些程序是交互式的,因此临时文件不起作用。

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

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

我更喜欢 POSIX shell 解决方案,或者至少可以在 cygwin 上的 bash 中工作。

感谢您的回答,我想出了以下解决方案。 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"

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

在 shell 2 中:

bash-3.2$ nc localhost 47001

在 shell 3 中:

bash-3.2$ nc localhost 47002

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

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

以上已经在 Cygwin 上测试过

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

【问题讨论】:

    标签: unix shell pipe


    【解决方案1】:

    命名管道怎么样?

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

    对于您的第二部分,我相信 tee 是正确的答案。于是就变成了:

    # A < foo | tee logfile | B > foo
    

    【讨论】:

      【解决方案2】:

      【讨论】:

      • twinpipe 还附带了一个使用其他人建议的命名管道方法的 shell 脚本。谢谢
      【解决方案3】:

      你或许可以摆脱命名管道:

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

      【讨论】:

        【解决方案4】:

        您可以使用Expect

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

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

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

        【讨论】:

          【解决方案5】:

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

          cmd1 |& cmd2 >&p <&p
          

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

          【讨论】:

            【解决方案6】:

            我曾经遇到过这个问题,我把这个简单的 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】:

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

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

              【讨论】:

              • 恕我直言,这是最好/唯一合理的解决方案......虽然它似乎确实需要 bash(而不是 dash / sh)
              【解决方案8】:

              我建议使用“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
              

              然后看这个会话:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-12-09
                • 2014-01-19
                • 1970-01-01
                • 1970-01-01
                • 2023-04-10
                • 2017-04-09
                • 2014-11-25
                • 2011-12-26
                相关资源
                最近更新 更多