web-dev-qa-db-ja.com

プロセスの標準入力への書き込み

私が次のように入力するかどうか私が理解する限り...

 python -i

... python-interpreterはstdinから読み取り、(明らかに)次のように動作します。

 >>> print "Hello"
 Hello

私がこれをするなら、私はそれが同じことをすることを期待するでしょう:

 echo 'print "Hello"' > /proc/$(pidof python)/fd/0

しかし、これは出力です(実際には空の行になっています)。

 >>> print "Hello"
 <empyline>

これは私にとってはprint "Hello"\nstdoutに書き込みましたが、まだ解釈されていません。なぜそれが機能しないのですか?それを機能させるために何をしなければなりませんか?

10
Sheppy

送信この方法でシェル/インタープリターに入力すると、問題が発生しやすく、信頼できる方法で作業することが非常に困難になります。

適切な方法はソケットを使用することです。これがソケットが発明された理由です。ncatncまたはsocatを使用してコマンドラインでこれを行うことができ、python単純なソケットに処理するか、ポートにバインドしてソケットで解釈するコマンドを待機する単純なpythonアプリケーションを記述します。

ソケットはローカルにすることができ、Webインターフェースに公開されません。


問題は、コマンドラインからpythonを起動すると、通常、ターミナルに接続されているシェルに接続され、実際には

$ ls -al /proc/PID/fd
lrwxrwxrwx 1 USER GROUP 0 Aug 1 00:00 0 -> /dev/pty1

したがって、Pythonのstdinに書き込む場合、実際には、単純なファイルではなく、カーネルデバイスであるpty疑似端末に書き込みます。 ioctlおよびreadではなくwriteを使用するため、画面に出力が表示されますが、生成されたプロセス(python)には送信されません。

試行している内容を複製する1つの方法は、fifoまたはnamed pipeを使用することです。

# make pipe
$ mkfifo python_i.pipe
# start python interactive with pipe input
# Will print to pty output unless redirected
$ python -i < python_i.pipe &
# keep pipe open 
$ sleep infinity > python_i.pipe &
# interact with the interpreter
$ echo "print \"hello\"" >> python_i.pipe

入力のみにscreenを使用することもできます

# start screen 
$ screen -dmS python python
# send command to input
$ screen -S python -X 'print \"hello\"'
# view output
$ screen -S python -x
9
crasic

_/proc/PID/fd/0_にアクセスすると、プロセスのファイル記述子0にアクセスできません[〜#〜] pid [〜#〜]、ファイルにアクセスします[〜#〜] pid [〜#〜]はファイル記述子0で開いています。これは微妙な違いですが、重要です。ファイル記述子は、プロセスがファイルに持つ接続です。ファイル記述子への書き込みは、ファイルがどのように開かれたかに関係なく、ファイルに書き込みます。

_/proc/PID/fd/0_が通常のファイルの場合、それに書き込むとファイルが変更されます。データは必ずしもプロセスが次に読み取るものではありません。プロセスがファイルを読み取るために使用しているファイル記述子にアタッチされた位置によって異なります。プロセスが_/proc/PID/fd/0_を開くと、他のプロセスと同じファイルを取得しますが、ファイルの位置は独立しています。

_/proc/PID/fd/0_がパイプの場合、それに書き込むと、パイプのバッファーにデータが追加されます。その場合、パイプから読み取るプロセスがデータを読み取ります。

_/proc/PID/fd/0_がターミナルの場合、それに書き込むoutputsターミナル上のデータ。ターミナルファイルは双方向です。ファイルに書き込むとデータが出力されます。つまり、ターミナルにはテキストが表示されます。端末からの読み取りはデータを入力します。つまり、端末はユーザー入力を送信します。

Pythonは端末に対して読み取りと書き込みの両方を行います。 echo 'print "Hello"' > /proc/$(pidof python)/fd/0を実行すると、_print "Hello"_が端末に書き込まれます。端末は指示どおりに_print "Hello"_を表示します。 pythonプロセスは何も表示せず、まだ入力を待っています。

入力をPythonプロセスにフィードする場合は、ターミナルにそれを実行させる必要があります。その方法については crasic's answer を参照してください。

Gillesの発言 を基にして、端末に接続されているプロセスの標準入力に書き込みたい場合は、実際に情報を端末に送信する必要があります。ただし、端末は入力および出力の形式として機能するため、端末に書き込む場合、「画面」ではなく、端末内で実行中のプロセスに書き込みを行うことを端末が認識できません。

ただし、LinuxにはTIOCSTI(ターミナルI/Oコントロール-端末入力のシミュレーション)と呼ばれるioctlリクエストを介してユーザー入力をシミュレートする非posixの方法があり、文字を入力したかのように端末に文字を送信できます。ユーザー。

私はこれがどのように機能するかを表面的にしか認識していませんが、 this の回答に基づいて、

import fcntl, sys, termios

tty_path = sys.argv[1]

with open(tty_path, 'wb') as tty_fd:
    for line in sys.stdin.buffer:
        for byte in line:
            fcntl.ioctl(tty_fd, termios.TIOCSTI, bytes([byte]))

一部の外部リソース:

http://man7.org/linux/man-pages/man2/ioctl.2.html

http://man7.org/linux/man-pages/man2/ioctl_tty.2.html