Python3.7で外部プロセス実行(Windows環境での動作確認)

はじめに

Pythonのsubprocessモジュールで外部プロセス実行した場合の動作確認メモ。
Windows10で試しました。

環境

  •  Windows10 Home edition 64bit
  •  Python 3.7.1

コマンド実行(正常系)

公式ドキュメントによるとPython3.5からはrun関数推奨とのことなので、run関数で実行すると(Python3.6から導入されたf-stringもついでに使ってみる)

import subprocess
cp = subprocess.run(["ping", "-n", "1", "127.0.0.1"],  check=True, capture_output=True)
print(f'type: {type(cp)}')
print(f'return code: {cp.returncode}')
print(f'stderr:{cp.stderr.decode("cp932")}')
print(f'stdout:{cp.stdout.decode("cp932")}')

以下のような出力が得られます。

type: <class 'subprocess.CompletedProcess'>
return code: 0
stderr:
stdout:
127.0.0.1 に ping を送信しています 32 バイトのデータ:
...
    最小 = 0ms、最大 = 0ms、平均 = 0ms

ここでのポイントは以下の4点です。

  • run関数はプロセス実行完了を待ち合わせること
  • run関数はCompletedProcessインスタンスを返すこと
  • CompletedProcessは戻り値や標準出力を属性として保持していること
  • CompletedProcess.stdoutはバイト列なのでdecodeする必要があること

コマンド実行(異常系)

不正な引数値(check=Falseで)

ping -nオプションに数値以外を与えて、異常終了させます。

import subprocess
cp = subprocess.run(["ping", "-n", "x", "127.0.0.1"], check=False, capture_output=True)
print(f'return code : {cp.returncode}')
print(f'stdout:{cp.stdout.decode("cp932")}')
print(f'stderr:{cp.stderr.decode("cp932")}')

以下の出力が得られます。

return code : 1
stdout:オプション -n の値が無効です。1 から 4294967295 の値を指定してください。
stderr:

ここでのポイントはコマンドは異常終了していますが、check=Falseとしているため、例外が発生していない点です。

不正な引数値(check=Trueで)

ping -nオプションに数値以外を与えて、異常終了させます。今回はcheck=Trueにしてみます。

import subprocess
cp = subprocess.run(["ping", "-n", "x", "127.0.0.1"], check=True, capture_output=True)

以下の出力が得られます。

Traceback (most recent call last)
  File "C:/Users/foo/sample.py", line 3, in 
    cp = subprocess.run(["ping", "-n", "x", "127.0.0.1"], check=True, capture_output=True)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 481, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['ping', '-n', 'x', '127.0.0.1']' returned non-zero exit status 1.

ここでのポイントはcheck=Trueとしているため、CalledProcessError例外が発生する点です。

存在しないコマンド(check=True, shell=Falseで)

以下のように存在しないコマンドhogeを実行します。

import subprocess
cp = subprocess.run(["hoge"], check=False, shell=False, capture_output=True)

以下の出力が得られます。

Traceback (most recent call last):
  File "C:/Users/foo/sample.py", line 3, in 
    cp = subprocess.run(["hoge"], check=False, shell=False, capture_output=True)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 466, in run
    with Popen(*popenargs, **kwargs) as process:
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 769, in __init__
    restore_signals, start_new_session)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 1172, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。

ここでのポイントはcheck=Falseとしているにも関わらず、FileNotFoundError例外が発生する点です。

存在しないコマンド(check=True, shell=Trueで)

以下のように存在しないコマンドhogeを実行します。今度はcheck=True, shell=Trueで実行します。

import subprocess
cp = subprocess.run(["hoge"], check=True, shell=True, capture_output=True)

以下の出力が得られます。

Traceback (most recent call last):
  File "C:/Users/foo/sample.py", line 3, in 
    cp = subprocess.run(["hoge"], check=True, shell=True, capture_output=True)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 481, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['hoge']' returned non-zero exit status 1.

ここでのポイントはcheck=True,shell=TrueとしたことでCalledProcessErrorが発生するようになった点です。

コマンド実行(タイムアウト)

タイムアウト付きで実行してみます。

import subprocess
subprocess.run(["ping", "-t", "127.0.0.1"],  capture_output=True, timeout=5)

以下の出力が得られます。

Traceback (most recent call last):
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 468, in run
    stdout, stderr = process.communicate(input, timeout=timeout)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 933, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "C:\Users\foo\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 1263, in _communicate
    raise TimeoutExpired(self.args, orig_timeout)
subprocess.TimeoutExpired: Command '['ping', '-t', '127.0.0.1']' timed out after 5 seconds

ここでのポイントはtimeout=5としたことでTimeoutExpiredが発生するようになった点です。

まとめ

  • 外部プロセス実行で完了を待ち合わせて良く標準出力の量もさほど多くなければ、run関数で実行する(それ以外で細かく制御したい時はsubprocess.Popenを使う)
  • 外部プロセスの実行エラーはcheck=TrueとするとCalledProcessError例外が発生する
  • 存在しないコマンド指定時はcheck=FalseとしていてもFileNotFoundError例外が発生する。

参考

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA