Hallo zusammen,
viele User haben sich gefragt, wie man Daten oder Anweisungen von einem Programm zu einem anderen übertragen kann.
Die klassische Lösung besteht wohl darin, dass das eine Programm eine Datei schreibend öffnet und dort ihr Anliegen ablegt. Das andere Programm schaut dann mal nach, liest aus, wertet aus, führt aus.
Problematisch wird's, wenn gleichzeitig geschrieben und gelesen werden soll. Solche Sachen programmtechnisch zu regeln, ist zwar mal ganz interessant - aber lästig wie Fliegen im Sommer.
Die Lösung, die ich heute zeigen will, nutzt sog. named pipes was gleichbedeutend mit FIFO-Dateien ist. FIFO steht für First in - first out. Also was zuerst hereinkommt = geschrieben wird, kommt auch zuerst heraus = ausgelesen. Das HERAUS ist wörtlich zu nehmen. Die Datei ist danach leer!
Hier die beiden Dateien:
IPC_Master.icn
procedure main()
remove("fifo_test")
system("mkfifo fifo_test")
system("/home/andreas/icon9_51/bin/IPC_Servant &")
repeat
{ befehl := case ?6 of
{1: "df"
2: "ls -al"
3: "dir"
4: "dmesg"
5: "top -n1"
6: "ping -c5 127.0.0.1"
}
write("Befehl = ", \befehl)
system("echo " || \befehl || " > fifo_test &")
if \befehl then delay(2500)# + ?10000)
}
end
Alles anzeigen
und IPC_Servant.icn
procedure main()
write("IPC_Servant gestartet")
repeat
{ if fh := open("fifo_test", "r") then
{ z := read(fh)
write("IPC_Servant: ", \z)
system(z)
close(fh)
}
delay(2500)
}
write("IPC_Servant beendet")
end
Alles anzeigen
Erläuterungen und Deutungen zum Quellcode
Die FIFO-Datei heiße fifo_test. Diese wird zunächst gelöscht. Die Erfahrung hat gezeigt, dass Fehlermeldungen kommen, wenn diese Datei als named pipe geöffnet werden soll und bereits existiert.
Durch Aufrufen des Linux-Kommandos mkfifo wird die danach genannte Datei als FIFO-Datei nutzbar gemacht. Dass diese Dateiart etwas besonderes darstellt, wird schon dadurch deutlich, wenn man sich das Ergebnis dieses Befehls über
anschaut:
Das p in der ersten Spalte macht den Unterschied zu herkömmlichen Dateien (-) oder Verzeichnissen (d).
Danach wird das Gegenstück zu diesem Programm aufgerufen: IPC_Servant. Wichtig ist hier die Verwendung des Symbols & - darüber wird der angegebene Befehl im Hintergrund ausgeführt. Wichtig hierbei ist aber, dass sich IPC_Master und IPC_Servant die gleiche Standard-Ausgabe "teilen".
Danach geht's in die Endlosschleife.
Eines von 6 Linux-Kommandos wird zufällig (Zufallszahlen-Operator ?) ausgewählt und in Abhängigkeit dieser Zufallszahl die Variable befehl mit einer Zeichenkette belegt.
Dieser Befehl wird zur Kontrolle ausgegeben. Wichtiger ist aber das Speichern in der named pipe fifo_test. Auch hier kommt das &-Symbol zum Einsatz. Würde man dieses Symbol weglassen, würde die FIFO-Sache nicht funktionieren. (Ausprobieren macht AHA!). Das hängt damit zusammen, dass Schreiben erst mit dem Auslesen beendet werden kann. Das Ergebnis ist nämlich eine leere Datei fifo_test. Das klingt im Vergleich mit herkömmlichen Datei-Operationen (Schreiben, Lesen) widersinnig. Aber FIFO bedeutet hier im wahrsten Sinne des Wortes: FIRST IN => FIRST OUT. Somit läuft das Schreiben im Hintergrund, das bereits im Hintergrund laufende IPC_Servant kann erkennen, dass da was hineingeschrieben wurde. Sobald die Daten ausgelesen wurden, ist dann auch der Schreibvorgang abgeschlossen.
Hier habe ich noch den Operator \ eingesetzt. Dies bedeutet soviel wie: Wenn der danach folgende Ausdruck zu einem Ergebnis führt, das nicht &fail und nicht &null ist, dann führe den Ausdruck davor mit dem auf diese Weise versehenen Ausdruck aus. Wenn der Ausdruck keinen Wert liefert (&null) oder bei der Auswertung dieses Ausdrucks ein Fehler zurück gemeldet wird (&fail), dann wird diese Funktion davor nicht ausgeführt - denn ein Fehler ist da im wahrsten Sinne des Wortes vorprogrammiert . Und wiederum wäre es nervig, Code zu programmieren, um solche Sachen abzufangen. \ und / sind quasi Icon's Exception-Programmierung! Ein einziges Zeichen statt mehrere Zeilen...
Dann wird noch für 2,5 Sekunden getrödelt (die CPU dankt's). Den Wert habe ich so hoch gesetzt, damit man die Programmausgabe des Programms IPC_Servant auch noch erkennen kann.
IPC_Servant macht dann Folgendes:
Eine kurze Mitteilung, dass das Programm gestartet wurde, wird vor der Endlos-Schleife abgesetzt.
Die Datei fifo_test wird zum Lesen geöffnet und die Zeile mit dem Kommando, das IPC_Master hinein geschrieben hat, ausgelesen. Denkt immer dran: Die Datei ist genau jetzt LEER - aber noch geöffnet! Es kann zwar nichts mehr herausgelesen werden, aber jederzeit wieder was hineingeschrieben werden.
Die ausgelesene Zeile mit dem Kommando wird auf dem Bildschirm angezeigt, über system() ausgeführt und die Datei wieder geschlossen.
Anmerkung: Will man eine Pipe (Eingabe-Ausgabe-Umleitung) ausführen lassen und / oder das Ergebnis einer Pipe oder eines simplen Linux-Kommandos erhalten, geht das nicht mehr mit dem Befehl system(). Hier muss man eine Pipe öffnen:
p := []
if pipe := open("Kommando1 | Kommando2 | ... | KomnmandoN", "p") then
{ while put(p, read(pipe))
close(pipe)
}
Exkurs:
Die Zeile
möchte ich noch beleuchten. Der Form nach handelt es sich hierbei um eine Endlosschleife ohne formale Abfrage eines Abbruchs - sollte man also nicht machen. Dies ist in der Funktion read() versteckt - die Funktionalität dahinter ist Icon's goal directed-Konzept geschuldet.
Die Funktion read(pipe) liest eine Zeile aus einer Datei, die dem Filehandle pipe zugewiesen ist. Das Ergebnis ist eine Zeichenkette, wenn es geklappt hat - oder &null, wenn's da nichts zu Lesen gab, d.h. der Lesezeiger auf dem Ende der Datei liegt.
Das funktioniert vielleicht ein zweites und ein drittes Mal. Jede Zeile wird in der Liste p abgelegt. In anderen Programmiersprachen kennt man das als Array. Die Listenbehandlung in Icon ist allerdings sehr flexibel. Wenn ich als Programmierer keine Vorstellung habe, wie viele Elemente denn eine solche Liste aufnehmen können soll, dann definiere ich eine solche Liste einfach durch
oder
und denke mir dabei "Räscheknäscht, kümmere Du Dich um die Verwaltung. Wenn über put() ein neues Element eingefangen wird, dann erhöhe die Anzahl der Elemente in der Liste p. Wie Du das machst, ist mir sowas von egal. Ebenso, wenn die Liste vorne oder hinten angeknabbert wird. Ich will nur über Indizes einen Zugriff auf gültige Elemente haben."
In den seltenen Fällen, in denen ich den Listenumfang kenne, dann schreibe ich so
wenn es sich um 17 Elemente handeln soll.
Und wenn die 17 Elemente den gleichen Startwert erhalten sollen, dann so:
oder
Anmerkung: Mehrdimensionale Listen gibt's in Icon natürlich auch. Die nennen sich wie in anderen Programmiersprachen auch Matrizen. Mehr dazu ist in den Icon-Tutorials verbuddelt.
Irgendwann ist das Dateiende erreicht. read(pipe) scheitert und liefert &null bzw. &fail zurück. Da dieser Fehler hier nicht abgefangen wird, schlägt er sich weiter durch.
==>
==>
put() scheitert dann auch und liefert nicht mehr das neue Element sondern ... na ja ... auch wieder &fail
==>
bricht dann die Endlosschleife sofort und direkt ab.
Auch wieder so ein klassischer Einzeiler, der in anderen Programmiersprachen eine Schleife mit "hin und her", "falls und falls doch nicht erfordern würde.
Exkurs-Ende
In der Liste p befinden sich dann die ausgelesenen Ergebnisse.
- Die Liste p kann leer sein =Kein Ergebnis
- Die Liste p kann ein Element enthalten = Ein Ergebnis
- Die Liste p kann beliebig viele Elemente enthalten = Mehrere Ergebnisse, die entweder über Erweiterung der Pipe oder innerhalb des eigenen Programmes ausgewertet werden sollen.
Falls jemand die Anzahl der Elemente in der Liste = Größe der Liste wissen möchte, das geht über den Größen-Operator *
Danach wird dann wieder die Zeit tot geschlagen.
Das eine Programm schreibt, das andere liest aus und führt aus. Kurz, knapp, bündig, stabil, ...
Beste Grüße
Andreas