Python运行守护子进程并读取标准输出

14

我需要运行一个程序并收集它的标准输出。这个程序(socat)需要在Python脚本的整个过程中在后台运行。一旦运行,socat就会进入守护进程模式,但首先它会输出一些行到stdout,我需要这些行来进行脚本的后续操作。

命令:socat -d -d PTY: PTY:

输出:

2011/03/23 21:12:35 socat[7476] N PTY is /dev/pts/1
2011/03/23 21:12:35 socat[7476] N PTY is /dev/pts/2
2011/03/23 21:12:35 socat[7476] N starting data transfer loop with FDs [3,3] and [5,5]

我基本上想要在程序开始时运行它并一直保持运行状态,直到脚本终止,但我需要将两个/dev/pts/X名称读入Python中。

有人能告诉我如何做吗?

我想出了下面的代码,但它一直挂起,我猜测是因为它在等待子进程终止。

#!/usr/bin/python
from subprocess import Popen, PIPE, STDOUT

cmd = 'socat -d -d PTY: PTY: &'

p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
output = p.stdout.read()

# Process the output 
print(output)
感谢任何帮助。
编辑:似乎它可以写入stderr,但是脚本仍然会挂起,使用和不使用&时都读取stderr。

1
没有 & 会工作吗? - Marcelo Cantos
不行 :( 很遗憾,它只是卡住了。 - Jason
你尝试过添加打印语句来验证它是否在调用Popen后阻塞,而不是在read()调用上阻塞吗? - Jeff
socat 会产生大量输出还是只有几行? - Winston Ewert
3个回答

29
#!/usr/bin/python
from subprocess import Popen, PIPE
import pty
import os

cmd = 'socat -d -d PTY: PTY:'

master, slave = pty.openpty()

p = Popen(cmd, shell=True, stdin=PIPE, stdout=slave, stderr=slave, close_fds=True)
stdout = os.fdopen(master)
print stdout.readline()
print stdout.readline()

你的版本有两个问题。首先,你调用了没有参数的read函数,这意味着它会尝试读取所有内容。但是由于socat不会终止,它永远无法确定是否已经读取完所有内容。通过使用readline,Python只会读取到换行符为止。根据我对你的问题的理解,这正是你所需要的。
第二个问题是C标准库会在管道上缓冲输出。我们通过使用openpty()函数创建一个pty,并将其传递给子进程的stdout和stderr来解决这个问题。我们使用fdopen将该文件描述符转换为常规的Python对象,并且消除了缓冲。
我不知道你在做什么socat,但我想知道是否可以使用pty模块来替代它。你正在将一个pty复制到另一个pty,而openpty则创建了一对pty。也许你可以直接使用它们?

非常感谢,这个完美地解决了问题。我会研究一下是否可以用内置的pty模块替换socat。 - Jason
这真是救命稻草! - boxed

1
子进程可能永远不会关闭标准输出,因此read()调用将永远等待。更糟糕的是,当它发现它是一个管道而不是控制台时,它可能会缓冲其输出(标准C库会自动执行此操作,因此这不是应用程序编写得多么聪明的函数)。如果是这样,可能唯一的选择是使用类似Pexpect的expect-style库。

0
如果您为stdout和stderr提供了一个管道,我认为子进程将会阻塞等待管道被读取。.read()应该读取到EOF,我想。这两个问题可能会导致死锁。
然而,我认为由于.read()将读取直到EOF,即使没有stderr管道,它也会阻塞。
我认为您需要将读取放在一个线程中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接