读取输出时Python子进程通信冻结 [英] Python subprocess communicate freezes when reading output

查看:62
本文介绍了读取输出时Python子进程通信冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Gphoto2 在 DSLR 上拍照.由于它基于 bash 命令,我尝试使用 subprocess.communicate 但它在相机拍照后冻结.

I'm using Gphoto2 to take pictures on a DSLR. As its based on bash commands I tried to use subprocess.communicate but it freezes after the camera takes a picture.

如果我在终端中尝试 gphoto2 --capture-image-and-download 需要不到 2 秒.我正在研究 Raspberry Pi.

If I try the gphoto2 --capture-image-and-download in the terminal it takes less than 2 seconds. I'm working on a Raspberry Pi.

代码:

import subprocess

class Wrapper(object):

    def __init__(self, subprocess):
        self._subprocess = subprocess

    def call(self,cmd):
        p = self._subprocess.Popen(cmd, shell=True, stdout=self._subprocess.PIPE, stderr=self._subprocess.PIPE)
        out, err = p.communicate()
        return p.returncode, out.rstrip(), err.rstrip()


class Gphoto(Wrapper):
    def __init__(self, subprocess):
        Wrapper.__init__(self,subprocess)
        self._CMD = 'gphoto2'

    def captureImageAndDownload(self):
        code, out, err = self.call(self._CMD + " --capture-image-and-download")
        if code != 0:
            raise Exception(err)
        filename = None
        for line in out.split('\n'):
            if line.startswith('Saving file as '):
                filename = line.split('Saving file as ')[1]
        return filename


def main():
    camera = Gphoto(subprocess)

    filename = camera.captureImageAndDownload()
    print(filname)

if __name__ == "__main__":
    main()

如果我退出,我会得到这个:

If I exit I get this:

Traceback (most recent call last):
  File "test.py", line 39, in <module>
   main()
  File "test.py", line 35, in main
    filename = camera.captureImageAndDownload()
  File "test.py", line 22, in captureImageAndDownload
    code, out, err = self.call(self._CMD + " --capture-image-and-download")
  File "test.py", line 11, in call
    out, err = p.communicate()
  File "/usr/lib/python2.7/subprocess.py", line 799, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.7/subprocess.py", line 1409, in _communicate
    stdout, stderr = self._communicate_with_poll(input)
  File "/usr/lib/python2.7/subprocess.py", line 1463, in _communicate_with_poll
    ready = poller.poll()
KeyboardInterrupt

有什么想法吗?

推荐答案

根据以上评论,我们提出了以下建议..communicate() 调用挂起程序,我怀疑这是因为执行的命令没有正确退出.

Based on the comments above, here's what we came up with. The .communicate() call hung the program, and much to my suspicion it was because the executed command didn't exit properly.

你可以用来解决这个问题的一件事是手动轮询进程是否完成并在进行过程中打印输出.
现在上面的要点是写在手机上的,所以它没有正确地使用它,但这里有一个示例代码,您可以使用它来捕获输出并手动轮询命令.

One thing you can use to get around this is by manually polling the process if it's finished and print the output as you go along.
Now the gist above was written on the phone so it didn't utelize this properly, but here's an example code you can use to catch the output as you go and manually poll the command.

import subprocess
from time import time
class Wrapper():
    def call(self, cmd):
        p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        O = ''
        E = ''
        last = time()
        while p.poll() is None:
            if time() - last > 5:
                print('Process is still running')
                last = time()
            tmp = p.stdout.read(1)
            if tmp:
                O += tmp
            tmp = p.stderr.read(1)
            if tmp:
                E += tmp
        ret = p.poll(), O+p.stdout.read(), E+p.stderr.read() # Catch remaining output
        p.stdout.close() # Always close your file handles, or your OS might be pissed
        p.stderr.close()
        return ret

需要注意的三个重要事项,使用 shell=True 可能变得糟糕,不安全和棘手.
我个人喜欢它,因为我在执行内容时很少处理用户输入或未知变量".但有几句话要注意 - 永远不要使用它!

Three important things to notice, using shell=True might be bad, unsafe and tricky.
I personally favor it because I rarely handle user input or "unknown variables" when I execute stuff. But a few words of caution - Never use it!

其次,如果你不需要将错误和常规输出分开,你也可以这样做:

Second being, if you don't need to separate error and regular output, you can also do:

Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

它会让你少担心一个文件句柄.

It will give you one less file handle to worry about.

最后,总是清空 stdout/stderr 缓冲区,并始终关闭它们.这两件事很重要.
如果您不清空它们,它们可能会自己挂起应用程序,因为它们已满并且 Popen 无法在其中放入更多数据,因此它会等待您(在最佳情况下)清空它们.
其次是不关闭这些文件句柄,这可能会使您的操作系统用完可能的文件句柄来打开(操作系统在任何给定时间只能打开一定数量的集体文件句柄,因此不关闭它们可能会使您的操作系统有点用不了).

Finally thing, ALWAYS empty the stdout/stderr buffers, and always close them. These two things are important.
If you don't empty them, they might themselves hang the application because they're full and Popen can't put more data in them, so it will wait for you (in best case scenario) to empty them.
Second being not closing those file handles, that might render your OS to run out of possible file handles to open (there's only a certain ammount of collective file handles a OS can have open at any given time, so not closing them could render your OS useless for a bit).

(注意:取决于您使用的是 Python2 还是 3,p.stdout.read() 可能会返回字节数据,这意味着 O= '' 应该是 O = b'' 代替 etc)

(note: Depending on if you're using Python2 or 3, p.stdout.read() might give you bytes data back, meaning O = '' should be O = b'' instead etc)

这篇关于读取输出时Python子进程通信冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆