据我所知,如果我输入以下内容......
python -i
... python 解释器现在将从 stdin 读取,行为(显然)如下:
>>> print "Hello"
Hello
如果我这样做,我希望它能做同样的事情:
echo 'print "Hello"' > /proc/$(pidof python)/fd/0
但这是输出(是一个实际的空行):
>>> print "Hello"
<empyline>
在我看来,它只是将其print "Hello"\n
写入 stdout
,但没有解释它。为什么它不起作用?我需要做什么才能使它起作用?
答案1
访问不访问进程的文件描述符0/proc/PID/fd/0
PID,它访问的文件PID在文件描述符 0 上已打开。这是一个微妙的区别,但很重要。文件描述符是进程与文件的连接。无论文件是如何打开的,写入文件描述符都会写入该文件。
如果是常规文件,则写入它会修改该文件。数据不一定是进程接下来要读取的内容:它取决于附加到进程用于读取文件的文件描述符的位置。当一个进程打开时,它会获取与其他进程相同的文件,但文件位置是独立的。/proc/PID/fd/0
/proc/PID/fd/0
如果是一个管道,则写入它会将数据附加到管道的缓冲区。在这种情况下,从管道读取的进程将读取数据。/proc/PID/fd/0
如果是终端,则写入它/proc/PID/fd/0
输出终端上的数据。终端文件是双向的:写入它会输出数据,即终端显示文本;从终端读取输入数据,即终端传输用户输入。
Python 既可以读取终端也可以写入终端。当您运行时echo 'print "Hello"' > /proc/$(pidof python)/fd/0
,您正在写入print "Hello"
终端。终端print "Hello"
按照提示显示。 python进程没有看到任何东西,它仍在等待输入。
如果你想向 Python 进程提供输入,你必须让终端来完成它。看克拉西奇的回答了解如何做到这一点。
答案2
发送以这种方式输入 shell/解释器很容易出现问题,并且很难以任何可靠的方式工作。
正确的方法是使用套接字,这就是发明它们的原因,您可以在命令行中使用ncat
nc
或socat
将 python 进程绑定到简单的套接字来执行此操作。或者编写一个简单的 python 应用程序,绑定到端口并侦听在套接字上解释的命令。
套接字可以是本地的,并且不暴露给任何 Web 界面。
问题是,如果你python
从命令行启动,它通常附加到你的 shell,它附加到终端,事实上我们可以看到
$ ls -al /proc/PID/fd
lrwxrwxrwx 1 USER GROUP 0 Aug 1 00:00 0 -> /dev/pty1
所以当你写入stdin
Python时,你实际上是在写入pty
伪终端,它是一个内核设备,而不是一个简单的文件。它使用ioctl
notread
和write
,因此您将在屏幕上看到输出,但它不会被发送到生成的进程 ( python
)
复制您正在尝试的内容的一种方法是使用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
答案3
以什么为基础构建吉尔斯说,如果我们想要写入附加到终端的进程的标准输入,我们实际上需要将信息发送到终端。但是,由于终端充当输入和输出的一种形式,因此在写入时,终端无法知道您想要写入在其中运行的进程而不是“屏幕”。
然而,Linux 有一种非 posix 方式通过称为(终端 I/O 控制 - 模拟终端输入)的 ioctl 请求来模拟用户输入,TIOCSTI
它允许我们将字符发送到终端,就像用户键入的一样。
基于这答案,应该可以通过以下方式来做到这一点:
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]))
一些外部资源: