Abrufen der Standardeingabe aus einer benannten Pipe

Abrufen der Standardeingabe aus einer benannten Pipe

Ich versuche, Python in einem Terminalfenster auszuführen und seine Standardeingabe von einer benannten Pipe umzuleiten. Dann schreibe ich in einem anderen Terminal in die benannte Pipe und lasse diesen Befehl in Python ausführen.

Schalter 1:

mkfifo p1
python < p1

Terminal 2:

echo -n "print \"Hello World\"" > p1

Was passiert, ist: Python druckt Hello Worldund wird beendet. Ich möchte Python weiterlaufen lassen, um den nächsten Befehl entgegenzunehmen. Wie mache ich das in der Shell?

Antwort1

Du brauchst

  • Führen Sie Python interaktiv aus, auch wenn sein Standardeingabefeld kein Terminal ist: Verwenden Siepython -i
  • Halten Sie das Schreibende der Pipe offen, sonst erkennt Python EOF und wird beendet.

Also:

python -i < p1

Und anderswo:

# open p1 on fd 3 of the shell process¹ for instance so stdout is
# left untouched in case you still want to write messages there.
exec 3> p1

# write something on fd 3. For instance with ksh/zsh's print
# builtin:
print -u3 '1j*1j'

# or for commands such as echo that can only print things
# on stdout (fd 1), duplicate that fd 3 to their fd 1:
echo '1j*1j' >&3 # short for 1>&3

# after echo (a builtin² in virtually all shells) returns, fd 1
# is restored to what it was before, but fd 3 remains open on
# the writing end of the pipe, so python still doesn't see EOF
# there.

echo normal message on stdout
echo 1+1 >&3 # more instructions for python
...
# and when done, close that file descriptor so python sees the EOF
# (assuming that fd 3 was the only file descriptor left open on
# the writing end of that pipe):
exec 3>&-

Gerade in Skripten bietet sich alternativ die Umleitung einer ganzen Befehlsgruppe an, statt das fd manuell mit exec³ zu öffnen und zu schließen.

{
  echo ... >&3
  ...
  ...
} 3> p1

¹ dass fd 3 von untergeordneten Prozessen und (außer in ksh93, das das Flag „Close-on-Exec“ setzt) ​​von anderen in diesen Prozessen ausgeführten Befehlen geerbt wird, sofern vorhanden.

² das funktioniert natürlich auch für nicht integrierte Befehle. Bei nicht integrierten Befehlen muss die Shell fd 1 nicht speichern und wiederherstellen, da die Umleitung nur im untergeordneten Prozess durchgeführt wird, der zur Ausführung des Befehls geforkt wird. Bei externen Befehlen, außer bei ksh93, das dies automatisch tut, möchten Sie dieses fd 3 vielleicht tatsächlich schließen, damit es nicht an sie und Hintergrundprozesse weitergegeben wird, die sie möglicherweise irgendwann erzeugen ( cmd >&3 3>&-).

³ Beachten Sie, dass in diesem Fall ksh93 das Flag „Close-on-Exec“ für dieses FD 3 nicht setzt.

Antwort2

Sie können es verwenden tail -f, um den FIFO nach echodem Schreiben darauf geöffnet zu halten.

tail -n1 -f p1 | python

Warum das funktioniert

pythonliest aus p1. Wenn das Ende der Datei erreicht ist, wird das Lesen beendet. Dies ist ein normales Verhalten beim Lesen von Dateien, auch wenn es sich bei der Datei um eine benannte Pipe handelt. tailmit dem -fFlag (follow) liest weiter aus einer Datei, nachdem deren Ende erreicht ist.

Antwort3

Sie müssen das gesamte Programm auf einmal senden.

Wenn Sie run aufrufen, python < p1wartet die Shell auf eine Eingabe, bevor sie Python aufruft. Das heißt, Python beginnt nicht einmal mit der Ausführungüberhauptbis der gesamte Datenstrom von der Shell gelesen wurde und dann vollständig an übergeben wird python.

Auch wenn es python -u p1stattdessen ausgeführt wird (d. h. ungepuffert und aus einer Datei lesen p1), pythonwird versucht, die gesamte Datei zu lesen, bevor es etwas davon ausführt.

Versuchen Sie dieses Experiment.

Schalter 1:

mkfifo p1
python < p1

Terminal 2:

cat > p1
print "Hello World"
print "Hello World"

Sie werden sehen, dass Sie mehrere Zeilen senden können, aber Python in Term 1 tut nichts. Drücken Sie nun ctrl+ D. Das gesamte Programm wird auf einmal ausgeführt.

Zusammenfassend lässt sich also sagen, dass Sie das gesamte Programm senden müssen, wenn Python aus einer Pipe lesen soll. Auf diese Weise können Sie Python nicht interaktiv verwenden.

Antwort4

Hier ist eine andere Lösung. Terminal 1:

alec@MacBook-Air ~/hello $ rm -f my.fifo; mkfifo my.fifo 
alec@MacBook-Air ~/hello $ cat my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Terminal 2:

alec@MacBook-Air ~/hello $ cat > my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Sie geben Ihre Befehle in Terminal 2 ein und sehen, wie sie in Terminal 1 angezeigt werden. Sie können die Ausgabe von Terminal 1 an einen anderen Prozess weiterleiten, z. B. cat my.fifo | node .... Wenn Sie fertig sind, schließen Sie stdinTerminal 2 mit Strg-D. Dadurch wird das Programm geschlossen my.fifound der catBefehl in Terminal 1 wird beendet.

verwandte Informationen