写入进程的标准输入

写入进程的标准输入

据我所知,如果我输入以下内容......

 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/0PID,它访问的文件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 ncsocat将 python 进程绑定到简单的套接字来执行此操作。或者编写一个简单的 python 应用程序,绑定到端口并侦听在套接字上解释的命令。

套接字可以是本地的,并且不暴露给任何 Web 界面。


问题是,如果你python从命令行启动,它通常附加到你的 shell,它附加到终端,事实上我们可以看到

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

所以当你写入stdinPython时,你实际上是在写入pty伪终端,它是一个内核设备,而不是一个简单的文件。它使用ioctlnotreadwrite,因此您将在屏幕上看到输出,但它不会被发送到生成的进程 ( python)

复制您正在尝试的内容的一种方法是使用fifonamed 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]))

一些外部资源:

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

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

相关内容