2008-09-26 2 views
10

Ich habe zwei (UNIX) Programme A und B, die lesen und schreiben von stdin/stdout.Verbinden der Eingabe _and_output zwischen zwei Befehlen in der Shell/Bash

Mein erstes Problem ist, wie man die Stdout von A an stdin von B und die Stdout von B an die stdin von A. Ie., etwas wie A | B aber ein bidirektionales Rohr. Ich vermute, dass ich das durch using exec to redirect lösen konnte, aber ich konnte es nicht zur Arbeit bringen. Die Programme sind interaktiv, so dass eine temporäre Datei nicht funktioniert. Das zweite Problem ist, dass ich jede Richtung duplizieren und ein Duplikat über ein Logging-Programm an stdout leiten möchte, so dass ich den (textlinienbasierten) Datenverkehr sehen kann, der zwischen den Programmen passiert. Hier kann ich mit Abschlag> (...) davonkommen, wenn ich das erste Problem lösen kann.

Beide Probleme scheinen, als hätten sie bekannte Lösungen, aber ich habe nichts gefunden.

Ich würde eine POSIX-Shell-Lösung bevorzugen, oder zumindest etwas, das in bash auf Cygwin funktioniert.

Dank Ihrer Antworten habe ich die folgende Lösung gefunden. Die A/B-Befehle verwenden nc, um zwei Ports zu hören. Das Protokollierungsprogramm verwendet sed (mit -u für ungepufferte Verarbeitung).

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" 

Dies überwacht die Verbindung zu Port 47001 und 47002 und echo allen Verkehr auf Standard-Ausgabe.

In Schale 2 tun:

bash-3.2$ nc localhost 47001 

In Schale 3 tun:

bash-3.2$ nc localhost 47002 

Nun Leitungen 2 in der Schale eingegeben wird geschrieben werden 3 und vice versa Schale und der Verkehr angemeldet 1 Schale , so etwas wie:

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

Die oben auf Cygwin wurde

getestet

Update: Das obige Skript funktioniert nach ein paar Tagen (!) Nicht mehr. Anscheinend kann es zum Stillstand kommen. Einige der Vorschläge in den Antworten können zuverlässiger sein.

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

Antwort

5

Sie könnten wahrscheinlich mit Named Pipes weg?

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

Für Ihren zweiten Teil glaube ich, Tee ist die richtige Antwort. So wird es:

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

Sie können Expect verwenden.

Expect ist ein Werkzeug zur Automatisierung interaktiver Anwendungen wie Telnet, FTP, Passwd, Fsck, Rlogin, Tipp usw.

Sie können den folgenden Code verwenden als Ausgangspunkt (von der genommen Explo Buch Erwarten) - es verbindet den Ausgang von proc1 mit dem Eingang des proc2 und umgekehrt, wie gewünscht:

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

Diese Frage ist ähnlich wie one Ich fragte vor. Die von anderen vorgeschlagenen Lösungen sollten Named Pipes verwenden, aber ich vermute, dass Sie sie nicht in Cygwin haben. Derzeit bleibe ich bei my own (attempt at a) solution, aber es erfordert /dev/fd/0, die Sie wahrscheinlich auch nicht haben.

Obwohl ich die Passing-Befehlszeilen-as-Strings Aspekt von twinpipe (erwähnt von JeeBee (139495)) nicht wirklich mag, könnte es Ihre einzige Option in Cygwin sein.

3

Ich verbrachte viel Zeit damit, gab es auf und entschied mich zuletzt für ksh (die Korn-Shell), was dies erlaubt.

cmd1 |& cmd2 >&p <&p 

wo |& ist eine (Rohr) Bediener einen Coprozess zu starten und &p ist Dateideskriptor dieses Coprozess.

2

Ich hatte dieses Problem an einem Punkt, und ich warf dieses einfache C-Programm zusammen.

#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

Ich würde vorschlagen, "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 

Dann sehen diese Sitzung:

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