Python 子进程通信在读取输出时冻结

2024-01-04

我正在使用 Gphoto2 在 DSLR 上拍照。因为它基于我尝试使用的 bash 命令subprocess.communicate但相机拍照后就冻结了。

如果我尝试gphoto2 --capture-image-and-download在终端中,只需不到 2 秒。我正在开发 Raspberry Pi。

Code:

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()

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

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()call 挂起了程序,我怀疑这是因为执行的命令没有正确退出。

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

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

使用时需要注意的三个重要事项外壳=真 https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess可能是糟糕的、不安全的和棘手的。
我个人很喜欢它,因为我在执行东西时很少处理用户输入或“未知变量”。但请注意几句话 - 切勿使用它!

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

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

它会让您少一个需要担心的文件句柄。

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

(note:取决于您使用的是Python2还是3,p.stdout.read()可能会给你返回字节数据,意思是O = ''应该O = b''相反等等)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 子进程通信在读取输出时冻结 的相关文章

随机推荐