2008-09-26 15 views
10

Tengo dos (UNIX) programas A y B que leen y escriben desde stdin/stdout.Entrada de conexión _and_output entre dos comandos en shell/bash

Mi primer problema es cómo conectar el stdout de A a stdin de B y el stdout de B al stdin de A. Es decir, algo así como A | B pero una tubería bidireccional. Sospecho que podría resolver esto por using exec to redirect pero no pude hacerlo funcionar. 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 mediante un programa de registro a stdout para que pueda ver el tráfico (en línea de texto) que pasa entre los programas. Aquí puedo salir con tee> (...) si puedo resolver el primer problema.

Ambos problemas parecen que deberían 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 usa sed (con -u para 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" 

Esta escucha para las conexiones al puerto 47001 y 47002 y ecos todo el tráfico a la salida estándar.

En la cáscara 2 do:

bash-3.2$ nc localhost 47001 

En la cáscara 3 do:

bash-3.2$ nc localhost 47002 

Ahora líneas entraron en la cáscara 2 se escribirá en la cáscara 3 y viceversa, y el tráfico registran en bombardear 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 un punto muerto. Algunas de las sugerencias en las respuestas pueden ser más confiables.

Respuesta

5

Usted probablemente podría salirse con canalizaciones con nombre:

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

¿Qué hay de una tubería con nombre?

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

Para su segunda parte creo que la respuesta es correcta. Entonces se vuelve:

# A < foo | tee logfile | B > foo 
4

Puedes usar Expect.

Expect es una herramienta para automatizar aplicaciones interactivas como telnet, ftp, passwd, fsck, rlogin, tip, etc.

Usted podría utilizar el siguiente código (tomado de la Exploración de esperar libro) como punto de partida - que conecta la salida del proc1 a la entrada de proc2 y viceversa, como usted pidió:

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

Esta pregunta es similar a one Pregunté antes. Las soluciones propuestas por otros fueron usar pipes con nombre, pero sospecho que no los tienes en cygwin. Actualmente me estoy quedando con my own (attempt at a) solution, pero requiere /dev/fd/0 que probablemente tampoco tengas.

Aunque realmente no me gusta el aspecto passing-command-lines-as-strings de twinpipe (mencionado por JeeBee (139495)), podría ser su única opción en cygwin.

3

Pasé mucho tiempo en esto, lo dejé, y finalmente decidí usar ksh (el shell Korn), que permite esto.

cmd1 |& cmd2 >&p <&p 

donde |& es un (tubo) del operador para iniciar un proceso de co-y &p es descriptor de archivo de que la co-proceso.

2

Tuve este problema en un punto, y armé 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") 
} 
0

me gustaría sugerir "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 

luego ver esta sesión:

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