名前付きパイプから標準入力を取得する

名前付きパイプから標準入力を取得する

私がやろうとしているのは、ターミナル ウィンドウで Python を実行し、その stdin を名前付きパイプからリダイレクトすることです。次に、別のターミナルで名前付きパイプに書き込み、そのコマンドを Python で実行します。

ターミナル1:

mkfifo p1
python < p1

ターミナル2:

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

何が起こるかというと、Python が出力されHello Worldて終了します。私がやりたいのは、次のコマンドを実行するために Python を実行し続けることです。これをシェルでどのように実行すればよいでしょうか?

答え1

必要がある

  • 標準入力がターミナルではない場合でもPythonを対話的に実行するには、次のようにします。python -i
  • パイプの書き込み側を開いたままにしておきます。そうしないと、Python は EOF を検出して終了します。

それで:

python -i < p1

そして他の場所:

# 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>&-

特にスクリプトでは、代わりに、exec³ を使用して fd を手動で開いたり閉じたりする代わりに、コマンド グループ全体をリダイレクトできます。

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

¹ fd 3 は子プロセスに継承され、(close-on-exec フラグを設定する ksh93 を除き) それらのプロセスで実行される他のコマンドにも継承されます (存在する場合)。

² もちろん、これは非組み込みコマンドにも機能します。非組み込みコマンドの場合、リダイレクトはコマンドを実行するためにフォークされた子プロセスでのみ実行されるため、シェルは fd 1 を保存して復元する必要はありません。外部コマンドの場合、自動的にそれを実行する ksh93 を除き、実際には fd 3 を閉じて、それらのコマンドや、最終的に生成される可能性のあるバックグラウンド プロセスに漏れないようにする必要があります ( cmd >&3 3>&-)。

³ その場合、ksh93 はその fd 3 に close-on-exec フラグを設定しないことに注意してください。

答え2

書き込みtail -f後に FIFO を開いたままにするために使用できます。echo

tail -n1 -f p1 | python

なぜこれが機能するのか

pythonから読み取り中ですp1。ファイルの終わりに達すると、読み取りを停止します。ファイルが名前付きパイプであっても、これはファイル読み取りの通常の動作です。 (follow) フラグtailを使用すると、ファイル-fの終わりに達した後もファイルからの読み取りが継続されます。

答え3

プログラム全体を一度に送信する必要があります。

runを呼び出すと、python < p1シェルはPythonを呼び出す前に入力を待ちます。つまり、Pythonは実行を開始しません。まったくデータ ストリーム全体がシェルによって読み取られ、そのまま に渡されるまで続きますpython

代わりに実行するpython -u p1と (つまり、バッファリングせずにファイルから読み取るとp1)、pythonファイルの一部を実行する前にファイル全体を読み取ろうとします。

この実験を試してみてください。

ターミナル1:

mkfifo p1
python < p1

ターミナル2:

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

複数行を送信できることがわかりますが、Term 1 の Python は何も実行しません。ここでctrl+を押しますD。プログラム全体が一度に実行されます。

つまり、要約すると、Python でパイプから読み取るには、プログラム全体を送信する必要があります。この方法では、Python を対話的に使用することはできません。

答え4

もう一つの解決策があります。ターミナル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 $ 

ターミナル2:

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

ターミナル 2 にコマンドを入力し、ターミナル 1 に表示されるのを確認します。ターミナル 1 の出力を別のプロセス (例 ) にパイプすることができますcat my.fifo | node ...。完了したら、stdinCtrl + D でターミナル 2 を閉じます。これにより、ターミナル 1 が閉じられmy.fifocatコマンドが終了します。

関連情報