【问题标题】:Start child process with subprocess.Popen and read its output while it is running使用 subprocess.Popen 启动子进程并在运行时读取其输出
【发布时间】:2018-02-12 09:30:13
【问题描述】:

我有一个命令,我将其输出写入文件并使用subprocess.Popen 调用该命令。这是一个示例:

stdout_output = open('testfile.txt','w')
process = subprocess.Popen(command,stdout=stdout_output,shell=True)
stdout_read = open('testfile.txt','r')
data = stdout_read.read()
print data

数据有时没有任何内容,但是当我在触发进程和读取它之间添加一些睡眠时,例如

stdout_output = open('testfile.txt','w')
process = subprocess.Popen(command,stdout=stdout_output,shell=True)
time.sleep(3)
stdout_read = open('testfile.txt','r')
data = stdout_read.read()
print data

然后数据包含写入文件中的实际数据。有什么我在这里想念的吗?或者有没有其他方法可以在触发进程和读取输出之间增加一些时间,而不是给硬编码的睡眠。

注意 - 该命令是一个持续的过程。我无法添加process.wait() 或无法等待进程完成后再读取文件。

【问题讨论】:

  • 你不能使用循环逐行阅读吗?

标签: python linux parallel-processing subprocess pipe


【解决方案1】:

Popen 启动一个并发运行的新进程,因此如果您想可靠地获得该进程的输出,那么是的,您必须等待该进程。事实上,subprocess 模块有一个 check_output 函数可以为您执行此操作:

data = subprocess.check_output(command, shell=True)
print data

显然这是阻塞的。


在不阻塞主进程的情况下“等待”进程结束的唯一方法是poll它。但是,这要求您以定期检查的方式编写代码,并且当poll 方法返回与None 不同的内容时,您可以读取该过程的输出。

例如:

def do_stuff(proc, filename):
    # proc.poll() check if proc has ended
    while proc.poll() is None:
        print('Here you do whatever you want while you wait for the process')
        # do other stuff
        ping_pong.play()
    # here we are sure the command terminate and wrote his output
    proc.stdout.close()
    with open(filename) as f:
        return f.read()

stdout_file = open('some_file', 'w')
process = Popen(['command'], stdout=stdout_file)
output = do_stuff(process, 'some_file')

根据您所做的工作,您可能需要以不同的方式构建代码。

【讨论】:

  • 这是阻塞的,所以我不能使用这种方法。
  • @user3551262 如果您不等到结束,您将无法读取进程输出...您唯一能做的就是poll 进程直到它结束...
  • 这可能有效。但是打开一个文件,创建写入它的进程,然后将文件名传递给一个打开同一文件但用于读取并返回文件内容的函数。似乎不是很干净的方法。最后,文件内容读取完成的部分仍然被阻塞,直到进程完成。
  • @ikac 您无法从未来读取文件,这很明显,这也是我已经尝试对 OP 说的话。您能做的最好的事情是做其他事情,直到“未来发生”并且您最终可以读取文件。显然,这样做是异步编程,可能需要对 Op 代码进行一些重大重构,如果没有该代码,我只能发布一个假示例。
【解决方案2】:

缓冲可能是个问题。

尝试使用零长度缓冲区打开文件进行写入。像这样:

stdout_output = open('testfile.txt','w', 0)

当然,命令可能不会立即产生输出,在这种情况下,您将需要有一个循环来不断尝试读取。

管道示例

由于您希望在启动过程后立即具有阅读能力,因此您可以使用Pipesubprocess.Popen 已经为您提供了将stdint/stdout/stderr 通过它的选项。

这是一个示例 Python 代码,其中包含示例 bash 脚本,该脚本回显消息、休眠,然后回显另一条消息。请注意,Python 代码必须知道子进程何时完成发送数据。

import subprocess

"""
notifier.sh
------------------

echo "This is me"
sleep 4
echo "This is me again"

------------------
"""

command = ['bash', 'notifier.sh']
process = subprocess.Popen(command, stdout=subprocess.PIPE)

while True:
    if process.poll() is not None:
        break
    data = process.stdout.readline()
    print data

我确实尝试使用此示例 bash shell 脚本来模拟用例。

另外,我确实删除了shell=True,因为我不确定是否有充分的理由使用它,但这是一个很大的安全问题。

【讨论】:

  • 看起来您需要在进程和 Python 代码的其余部分之间建立一个“管道”?我对吗?由于您打开相同的文件进行写入,然后立即进行读取。这可能会扰乱正在运行的进程的整个编写过程。
  • 你能给我一个样本吗
【解决方案3】:

如果您不想等到执行结束,其中一种选择是在单独的线程中读取:

def reader(fd, finished):
    while not finished.is_set():
        data = fd.read()
        if data: print(data)
        time.sleep(SOME_TIMEOUT)

process = subprocess.Popen(command,stdout=stdout_output,shell=True)
finished = threading.Event()
reader_thread = threading.Thread(target=reader, args=(stdout_output, finished))
reader_thread.start()
process.wait()
finished.set()
reader_thread.join()

【讨论】:

  • 我可以不使用线程,而是使用 while 循环并对其设置超时,直到它返回正确的输出。
猜你喜欢
  • 1970-01-01
  • 2014-11-02
  • 1970-01-01
  • 2013-01-25
  • 2011-07-26
  • 1970-01-01
  • 2016-06-21
  • 2011-07-21
  • 1970-01-01
相关资源
最近更新 更多