我有两个(UNIX)程序A和B,它们从stdin / stdout读取和写入。
我的第一个问题是如何将A的stdout连接到B 的stdin和将B的stdout连接到A.的stdin。即A | B但是双向管道。我怀疑我可以通过using exec to redirect来解决这个问题,但我无法让它发挥作用。程序是交互式的,因此临时文件不起作用。
第二个问题是我想复制每个方向并通过日志记录程序将副本传递给stdout,以便我可以看到在程序之间传递的(基于文本行的)流量。如果我能解决第一个问题,我可以在这里使用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上进行了测试
更新:上面的脚本在几天后停止工作(!)。显然它可以陷入僵局。答案中的一些建议可能更可靠。
答案 0 :(得分:10)
命名管道怎么样?
# mkfifo foo
# A < foo | B > foo
# rm foo
对于你的第二部分,我认为tee是正确的答案。所以它变成了:
# A < foo | tee logfile | B > foo
答案 1 :(得分:5)
答案 2 :(得分:5)
您可能可以使用命名管道:
mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe
答案 3 :(得分:4)
您可以使用Expect。
Expect是一种自动化交互式应用程序的工具,如telnet,ftp,passwd,fsck,rlogin,tip等。
您可以使用以下代码(取自 Exploring Expect 书籍)作为起点 - 它按照您的要求将proc1的输出连接到proc2的输入,反之亦然:
#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1
答案 4 :(得分:3)
我花了很多时间在这上面,放弃了,最后决定使用ksh(Korn shell),这允许这样做。
cmd1 |& cmd2 >&p <&p
其中|&
是启动协同进程的(管道)运算符,&p
是该协同进程的文件描述符。
答案 5 :(得分:2)
我曾经遇到过这个问题,我将这个简单的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")
}
答案 6 :(得分:0)
这个问题类似于我之前提到的one。其他人提出的解决方案是使用命名管道,但我怀疑你没有在cygwin中使用它们。目前我坚持my own (attempt at a) solution,但它需要/dev/fd/0
你可能也没有。
虽然我不太喜欢twinpipe
的传递 - 命令行 - 字符串方面(由JeeBee(139495)提及),但它可能是你在cygwin中的唯一选择。 / p>
答案 7 :(得分:0)
我建议“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 ...