0

Gphoto2 を使用してデジタル一眼レフで写真を撮っています。私が使用しようとしたbashコマンドに基づいているためsubprocess.communicate、カメラが写真を撮った後にフリーズします。

端末で試してみると、gphoto2 --capture-image-and-download2秒もかかりません。私はラズベリーパイに取り組んでいます。

コード:

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

何か案は?

4

1 に答える 1

5

上記のコメントに基づいて、ここに私たちが思いついたものがあります。呼び出しによってプログラムがハングしました.communicate()が、実行されたコマンドが適切に終了しなかったことが原因ではないかと疑っていました。

これを回避するために使用できることの 1 つは、プロセスが終了した場合にプロセスを手動でポーリングし、進行中に出力を印刷することです。
上記の要点は電話で書かれたため、これを適切に活用できませんでしたが、出力をキャッチしてコマンドを手動でポーリングするために使用できるコード例を次に示します。

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を使用することは、危険で、安全でなく、トリッキーである可能性があることに注意してください。
個人的には、実行時にユーザー入力や「未知の変数」を処理することはめったにないので、これを好みます。ただし、注意事項がいくつかあります - 絶対に使用しないでください。

次に、エラーと通常の出力を分離する必要がない場合は、次のこともできます。

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

これにより、心配するファイル ハンドルが 1 つ少なくなります。

最後に、常にバッファを空にしstdout/stderr、常に閉じます。この2点が重要です。
それらを空にしないと、アプリケーションがいっぱいになりPopen、それ以上データを入れることができないため、アプリケーション自体がハングする可能性があるため、(最良のシナリオでは) それらを空にするのを待ちます。
2 つ目は、これらのファイル ハンドルを閉じないことです。これにより、OS が開く可能性のあるファイル ハンドルが不足する可能性があります (OS が任意の時点で開くことができる集合ファイル ハンドルは一定量しかないため、それらを閉じないと OS がレンダリングされる可能性があります)。ちょっと使い物にならない)。

:Python2または3を使用しているかどうかに応じて、p.stdout.read()バイトデータが返される可能性があります。意味は代わりにO = ''なるはずですなど)O = b''

于 2017-01-04T14:28:58.470 に答える