Conexión de entrada _y_output entre dos comandos en shell / bash
Pregunta
Tengo dos (UNIX) programas A y B que leen y escriben desde stdin / stdout.
Mi primer problema es cómo conectar el stdout de A al stdin de B y el stdout de B al stdin de A. Es decir, algo como A | B pero una tubería bidireccional. Sospecho que podría resolver esto mediante usando exec para redireccionar pero no pude conseguirlo trabajar. Los programas son interactivos, por lo que un archivo temporal no funcionaría.
El segundo problema es que me gustaría duplicar cada dirección y canalizar un duplicado a través de un programa de registro a la salida estándar para que pueda ver el tráfico (basado en líneas de texto) que pasa entre los programas. Aquí puedo salir con tee > (...) si puedo resolver el primer problema.
Ambos problemas parecen tener soluciones bien conocidas pero no he podido encontrar nada.
Preferiría una solución de shell POSIX, o al menos algo que funcione en bash en cygwin.
Gracias a sus respuestas se me ocurrió la siguiente solución. Los comandos A / B usan nc para escuchar dos puertos. El programa de registro utiliza sed (con -u para el procesamiento sin búfer).
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"
Esto escucha la conexión al puerto 47001 y 47002 y envía todo el tráfico a la salida estándar.
En shell 2 hacer:
bash-3.2$ nc localhost 47001
En shell 3 hacer:
bash-3.2$ nc localhost 47002
Ahora las líneas ingresadas en el shell 2 se escribirán en el shell 3 y viceversa y el tráfico registrado en el shell 1, algo así como:
B->A: input to port 47001
A->B: input to port 47002
Lo anterior ha sido probado en Cygwin
Actualización: el script anterior dejó de funcionar después de unos días (!). Aparentemente puede llegar a un punto muerto. Algunas de las sugerencias en las respuestas pueden ser más confiables.
Otros consejos
¿Qué tal una tubería con nombre?
# mkfifo foo
# A < foo | B > foo
# rm foo
Para tu segunda parte, creo que la respuesta es la correcta. Así se convierte en:
# A < foo | tee logfile | B > foo
Probablemente podría salirse con la suya con tuberías:
mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe
Puede usar Expect .
Expect es una herramienta para automatizar aplicaciones interactivas como telnet, ftp, passwd, fsck, rlogin, tip, etc.
Puede usar el siguiente código (tomado del libro Exploring Expect ) como punto de partida: conecta la salida de proc1 con la entrada de proc2 y viceversa, como solicitó:
#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1
Pasé mucho tiempo en esto, lo dejé y decidí usar ksh (el caparazón de Korn), que permite esto.
cmd1 |& cmd2 >&p <&p
donde | & amp;
es un operador (pipe) para iniciar un co-proceso y & amp; p
es el descriptor de archivo de ese co-proceso.
Tuve este problema en un momento, y creé este sencillo programa en 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")
}
Esta pregunta es similar a una que pregunté antes . Las soluciones propuestas por otros eran usar tuberías con nombre, pero sospecho que no las tienes en cygwin. Actualmente me atengo a mi propia (intento de una) solución , pero requiere / dev / fd / 0
que probablemente tampoco tenga.
Aunque realmente no me gusta el aspecto de pasar líneas de comando como cadenas de twinpipe
(mencionado por JeeBee ( 139495 )), podría ser su única opción en cygwin.
Sugeriría " 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
Entonces ve esta sesión:
$ coproc ./dialog
$ ./dialog test < /dev/fd/${COPROC[0]} > /dev/fd/${COPROC[1]}
Answer to Question test is ...