Python 子进程通信在读取输出时冻结
Python subprocess communicate freezes when reading output
我正在使用 Gphoto2 在 DSLR 上拍照。由于它基于 bash 命令,我尝试使用 subprocess.communicate
但它在相机拍照后冻结。
如果我在终端中尝试 gphoto2 --capture-image-and-download
,它只需要不到 2 秒的时间。我正在研究 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()
如果我退出我得到这个:
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()
调用挂起程序,我怀疑这是因为执行的命令没有正确退出。
你可以用来解决这个问题的一个方法是手动轮询进程是否完成,并在你进行时打印输出。
现在上面的要点写在 phone 上,所以它没有正确地使用它,但这里有一个示例代码,您可以使用它来捕获输出并手动轮询命令。
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 可能很糟糕、不安全且棘手。
我个人喜欢它,因为我很少在执行操作时处理用户输入或 "unknown variables"。但请注意几句 - 切勿使用它!
其次,如果你不需要将错误和正常输出分开,你也可以这样做:
Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
它会让你少担心一个文件句柄。
最后,始终 清空 stdout/stderr
缓冲区,并始终关闭它们。这两点很重要。
如果您不清空它们,它们可能会自己挂起应用程序,因为它们已满并且 Popen
无法在其中放入更多数据,因此它将等待您(在最佳情况下)清空它们。
其次是不关闭那些文件句柄,这可能会使您的 OS 到 运行 无法打开可能的文件句柄(OS 只能打开一定数量的集体文件句柄任何给定时间,因此不关闭它们可能会使您的 OS 暂时无用)。
(注意:根据您使用的是 Python2 还是 3,p.stdout.read()
可能会返回字节数据,这意味着 O = ''
应该是 O = b''
而不是等)
我正在使用 Gphoto2 在 DSLR 上拍照。由于它基于 bash 命令,我尝试使用 subprocess.communicate
但它在相机拍照后冻结。
如果我在终端中尝试 gphoto2 --capture-image-and-download
,它只需要不到 2 秒的时间。我正在研究 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()
如果我退出我得到这个:
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()
调用挂起程序,我怀疑这是因为执行的命令没有正确退出。
你可以用来解决这个问题的一个方法是手动轮询进程是否完成,并在你进行时打印输出。
现在上面的要点写在 phone 上,所以它没有正确地使用它,但这里有一个示例代码,您可以使用它来捕获输出并手动轮询命令。
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 可能很糟糕、不安全且棘手。
我个人喜欢它,因为我很少在执行操作时处理用户输入或 "unknown variables"。但请注意几句 - 切勿使用它!
其次,如果你不需要将错误和正常输出分开,你也可以这样做:
Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
它会让你少担心一个文件句柄。
最后,始终 清空 stdout/stderr
缓冲区,并始终关闭它们。这两点很重要。
如果您不清空它们,它们可能会自己挂起应用程序,因为它们已满并且 Popen
无法在其中放入更多数据,因此它将等待您(在最佳情况下)清空它们。
其次是不关闭那些文件句柄,这可能会使您的 OS 到 运行 无法打开可能的文件句柄(OS 只能打开一定数量的集体文件句柄任何给定时间,因此不关闭它们可能会使您的 OS 暂时无用)。
(注意:根据您使用的是 Python2 还是 3,p.stdout.read()
可能会返回字节数据,这意味着 O = ''
应该是 O = b''
而不是等)