Collegamento di input _e_output tra due comandi in shell / bash
Domanda
Ho due programmi (UNIX) A e B che leggono e scrivono da stdin / stdout.
Il mio primo problema è come collegare lo stdout di A allo stdin di B e lo stdout di B allo stdin di A. Cioè, qualcosa come A | B ma un tubo bidirezionale. Sospetto di poterlo risolvere usando exec per reindirizzare ma non sono riuscito a ottenerlo lavorare. I programmi sono interattivi, quindi un file temporaneo non funzionerebbe.
Il secondo problema è che vorrei duplicare ogni direzione e reindirizzare un duplicato tramite un programma di registrazione a stdout in modo da poter vedere il traffico (basato su una riga di testo) che passa tra i programmi. Qui potrei cavarmela con tee > (...) se posso risolvere il primo problema.
Entrambi questi problemi sembrano avere soluzioni ben note ma non sono riuscito a trovare nulla.
Preferirei una soluzione shell POSIX, o almeno qualcosa che funzioni bash su cygwin.
Grazie alle tue risposte ho trovato la seguente soluzione. I comandi A / B usano nc per ascoltare due porte. Il programma di registrazione utilizza sed (con -u per l'elaborazione senza buffer).
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"
Ascolta la connessione alla porta 47001 e 47002 ed echo tutto il traffico verso l'output standard.
Nella shell 2 fare:
bash-3.2$ nc localhost 47001
Nella shell 3 fare:
bash-3.2$ nc localhost 47002
Ora le righe immesse nella shell 2 verranno scritte nella shell 3 e viceversa e il traffico registrato nella shell 1, qualcosa del tipo:
B->A: input to port 47001
A->B: input to port 47002
Quanto sopra è stato testato su Cygwin
Aggiornamento: lo script sopra ha smesso di funzionare dopo alcuni giorni (!). Apparentemente può deadlock. Alcuni dei suggerimenti nelle risposte potrebbero essere più affidabili.
Altri suggerimenti
Che ne dici di una pipe con nome?
# mkfifo foo
# A < foo | B > foo
# rm foo
Per la tua seconda parte credo che tee sia la risposta corretta. Quindi diventa:
# A < foo | tee logfile | B > foo
Probabilmente potresti cavartela con le named pipe:
mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe
Puoi utilizzare Expect .
Expect è uno strumento per l'automazione di applicazioni interattive come telnet, ftp, passwd, fsck, rlogin, tip, ecc.
Potresti usare il seguente codice (tratto dal libro Exploring Expect ) come punto di partenza - collega l'output di proc1 all'input di proc2 e viceversa, come richiesto:
#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1
Ho trascorso molto tempo su questo, ho rinunciato e alla fine ho deciso di usare ksh (la shell Korn), che lo consente.
cmd1 |& cmd2 >&p <&p
dove | & amp;
è un operatore (pipe) per avviare un co-processo e & amp; p
è il descrittore di file di quel co-processo.
Ho avuto questo problema ad un certo punto e ho messo insieme questo semplice programma 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")
}
Questa domanda è simile a one che ho chiesto prima . Le soluzioni proposte da altri erano di usare pipe denominate, ma sospetto che tu non le abbia in Cygwin. Al momento mi attengo alla la mia (prova a) soluzione , ma richiede / dev / fd / 0
che probabilmente non hai.
Anche se non mi piace molto l'aspetto delle righe di comando di passaggio di twinpipe
(menzionato da JeeBee ( 139495 )), potrebbe essere la tua unica opzione in cygwin.
Suggerirei " 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
Quindi vedi questa sessione:
$ coproc ./dialog
$ ./dialog test < /dev/fd/${COPROC[0]} > /dev/fd/${COPROC[1]}
Answer to Question test is ...