Bei rmcd handelt sich um eine Funktion aus dem Skript funktionen. Pro Rechner kommt je eine Pipe zum Senden und eine zum Empfangen zum Einsatz. Ein Prozess liest permanent die Sende-Pipe aus und lenkt die gelesenen Zeilen in einen Datenstrom, den in diesem Beispiel SSH an den entfernten Rechner schickt. Die Empfangsseite lenkt den eingehenden Datenstrom wiederum in die Empfangs-Pipe um und liest ihn dort aus. Sie verarbeitet die gelesenen Daten und schreibt das Ergebnis wiederum in die Sende-Pipe, um sie zum Ursprungsrechner zurückzusenden (Abbildung 3).

Abbildung 3: Der Aufbau zeigt die Kommunikation mit anderen Rechnern unter Zuhilfenahme von Pipes und diversen Bordwerkzeugen.
Die Kommunikation teilt sich in mehrere Bereiche auf. Im Wesentlichen geht es darum, Daten über einen Datenstrom zu empfangen, zu verarbeiten und einen entsprechenden Datenstrom zurückzusenden. Wie aber erhält man einen kontinuierlichen Datenstrom zwischen zwei Rechnern aufrecht, um darüber Kommandos und weitere Informationen zu senden?
Listener
Zum Auslesen bietet sich das Kommando tail -f an, da es sich erst auf Anweisung oder bei Empfang eines EOF beendet. Es genügt jedoch nicht, einen einzelnen Befehl umzulenken, da nach dem Abarbeiten des Kommandos ein EOF folgt. Eine Textdatei als Transportmittel zu verwenden, funktioniert ebenfalls nicht, da die Lage der Informationen sich ständig verschiebt und das Tail-Kommando keine validen Informationen mehr ausliest.
Das testen Sie, indem Sie in einer Session ein tail -f auf eine leere Datei absetzen und in einer zweiten Session die Ausgabe eines Befehls in diese Datei umlenken. Sie erhalten dann die Meldung file truncated. Die einzige Möglichkeit bestünde darin, alle Informationen, die der entfernte Rechner verarbeiten soll, an die Textdatei anzuhängen, wodurch diese jedoch unnötig anwachsen und Speicherplatz verbrauchen würde.
Die Lösung liefern benannte Pipes. Da der Prozess Daten nur dann in die Pipe schreibt, wenn die Gegenstelle sie zum selben Zeitpunkt ausliest, benötigt der Vorgang keinen zusätzlichen Speicherplatz für die Pipe – das Tail-Kommando liest die Daten immer an derselben Stelle aus der Pipe aus. Um eine Kommunikation zwischen den beiden Rechnern aufzubauen, benötigt man zwei benannte Pipes. Die eine empfängt den Datenstrom des entfernten Rechners und die zweite sendet die verarbeiteten Daten an das Zielsystem.
Den Datenstrom stellt die Ausgabe von tail -f mithilfe einer anonymen Pipe bereit. Diesen lenken Sie in eine SSH-Session um, die auf dem entfernten Rechner in die Empfangs-Pipe schreibt. Das Konstrukt realisieren Sie mit dem Aufruf aus Listing 7.
Listing 7
Datenstrom
$ tail -f Sende-Pipe | ssh -q Ziel "cat >Empfangs-Pipe"
Im nächsten Schritt geht es darum, in welcher Form die Daten das jeweilige Zielsystem erreichen. Eine relativ einfache Möglichkeit besteht darin, eine Funktion zu definieren, die die auszuführenden Kommandos in eine entsprechende Sequenz einbettet. In unserem Beispiel übernimmt das die Funktion rcmd, der wir als Argument das auszuführende Kommando übergeben.
Damit das Zielsystem erkennt, was mit den empfangenen Daten passieren soll, beginnt die Übertragung mit dem Marker BEGIN_CMD als Text, gefolgt vom auszuführenden Kommando. Das Zielsystem prüft beim Empfang einer Zeile, was für eine Zeichenkette das erste Argument enthält. Handelt es sich um BEGIN_CMD, leitet es das zweite Argument in eine Shell um. Diese lenkt das Ergebnis wiederum in die Pipe um und schickt es dem Absender zurück.
Der Vorteil dieser Methode besteht darin, dass aus dem Programm heraus keine Bearbeitung des Kommandos erfolgen muss. In Listing 8 erfolgt das Schreiben der Zeichenkette in eine Subshell, die dann die Kommandos ausführt und das Ergebnis auf dem Bildschirm ausgibt. Das landet dann beim entfernten Rechner als Antwort.
Listing 8
Ergebnis ausgeben
$ echo "cd /var/tmp; ls -l | wc -l" | /bin/bash
29
Möchten Sie mehrere Kommandos auf dem Zielsystem ausführen, jedoch nicht in dessen Standardverzeichnis, machen Sie für jedes davon eine Subshell auf. Damit agiert jedes Kommando auch in einer identische Umgebung (Listing 9). Wie Sie hier sehen, gilt das Zielverzeichnis /root nur bis zum Abarbeiten der Kommandosequenz. Der zweite Durchlauf zeigt das aktuelle Arbeitsverzeichnis des Elternprozesses an.
Listing 9
Umgebungspfad
# echo "cd /root ; pwd" | /bin/bash /root § echo "pwd"| /bin/bash /var/tmp
Um dieses Problem für eine Kommunikation zwischen zwei Systemen zu umgehen, erhält das Zielsystem zusätzlich zum eigentlichen Kommando noch die Anweisung, das aktuelle Arbeitsverzeichnis in eine temporäre Textdatei zu schreiben. Beim nächsten auszuführenden Befehl prüft der Prozess, ob es eine solche temporäre Datei gibt, liest diese gegebenenfalls aus und setzt das Verzeichnis entsprechend (Listing 10).
Listing 10
Verzeichnis setzen
# type rcmd
rcmd is a function
rcmd ()
{
setenv;
chkpipes;
if [ $? -ne 0 ]; then
return 1;
fi;
echo "BEGIN_CMD $@ ; { pwd >/tmp/lastpwd.$lhost ;}" > $sendpipe;
return $?
}





