python subprocess 試験対策

コマンドを使いたいときに便利なsubprocess

自分は最近PowerPointを操作するアプリを作った際に、

PowerPointの操作はC#プログラムで、
PowerPoint操作以外の部分はPythonで
という感じでアプリを作った際にsubprocessを使いました。

Pythonでsubprocessを使って、C#で作ったプログラム.exeに対してコマンドライン引数渡して実行みたいな

ってことで、今回はsubprocessについて

まずは ls コマンドを実行してみたいと思います。

import subprocess

subprocess.run(args=["ls", "-l"])

結果


-rw-r--r--  1 fuji  staff  241 10 14 21:46 test_subprocess.py

引数argsにlistでコマンドを渡してあげれば実行してくれます。

標準入力を求めるコマンドに対して

wcコマンドっていう、文字数をカウントしてくれるコマンドがあるんですが、ターミナルでwcってだけ打つと文字入力待機状態になるわけなんですが、そういったコマンドに対して、文字を送ることもできます。

command = subprocess.run(args=["wc"], 
                         input="hello", 
                         encoding="utf-8", 
                         stdout=subprocess.PIPE)

print(command.stdout)


>>>結果
       0       1       5

input="hello"

  • input引数は、外部コマンドに渡す標準入力(データ)を指定します。この場合、コマンドに渡される標準入力の内容は、文字列 "hello" です。

  • つまり、wcコマンドがhelloという文字列を受け取り、それを基に文字数や単語数を数えます。

encoding="utf-8"

  • encoding引数は、標準入力と標準出力を文字列として扱う際のエンコーディングを指定します。

  • ここではutf-8エンコーディングを指定しているため、Pythonは標準出力や標準入力をUTF-8文字列として処理します。これが指定されていない場合、デフォルトではバイト列(bytes型)として扱われるので、文字列として処理するためにエンコーディングを明示しています。

stdout=subprocess.PIPE

  • stdout引数は、コマンドの標準出力をどのように処理するかを指定します。subprocess.PIPEを指定すると、外部コマンドの標準出力をPythonプログラム内でキャプチャして利用できます。

  • このコードでは、wcの結果をキャプチャして、後で利用できるようにしています。

ってことで重要なところとしては
文字列渡すときはencoding指定してね
結果を受け取りたいときはsubprocess.PIPEっていうのをstdoutに渡してね

ってところです。

1. stdout があるなら stdin もある?

stdin もあります。stdoutがコマンドの出力をキャプチャするために使われるのに対して、stdinはコマンドに入力を提供するために使います。

例えば、コマンドにファイルの内容を入力として渡す場合、次のように stdin を使います:

import subprocess

with open('input.txt', 'r') as input_file:
    # 'wc' コマンドにファイルの内容を渡す
    command = subprocess.run(['wc'], stdin=input_file, stdout=subprocess.PIPE, encoding='utf-8')
    print(command.stdout)

stdoutにファイルを渡せばファイルに出力を書き込んでくれます。
ようは

ls -l > sample.txt

をする感じです。

import subprocess

# 'ls -l' コマンドの結果を 'output.txt' に書き込む
with open('output.txt', 'w') as output_file:
    subprocess.run(['ls', '-l'], stdout=output_file)

コマンドが成功したかどうか

import subprocess


command = subprocess.run(args=["ls"])

print(command.returncode)
>> 0

command = subprocess.run(args=["ls", "sample"]) # 存在しないディレクトリを指定

print(command.returncode)
>> 1

returncodeで取得できて、成功は 0, エラーは1

これでコマンドの成功失敗を判定することもできるし、ほかにも引数check=Trueにすることで例外を出してくれます。

import subprocess

command = subprocess.run(args=["ls", "sample"], check=True) # 存在しないディレクトリを指定

print(command.returncode)

>>>
Traceback (most recent call last):
  File "/**********", line 9, in <module>
    command = subprocess.run(args=["ls", "sample"], check=True) # 存在しないディレクトリを指定
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['ls', 'sample']' returned non-zero exit status 1.

非同期処理で行いたい

subprocess.Popen の使い方

subprocess.run ではなく、もっと細かい制御が必要な場合は subprocess.Popen を使う。例えば、非同期でプロセスを実行し、後からプロセスの状態を確認したり、プロセスを停止したりできます。

import subprocess

# 非同期で 'sleep 5' コマンドを実行
process = subprocess.Popen(['sleep', '5'])
# 他の処理を並行して行える
print("上記を実行しつつ次の処理を実行します")
subprocess.run(args=["echo", "Hello, World!"])

# プロセスの終了を待機
process.wait()
print("プロセスが終了しました")

実行するとわかるんですが、
コード上はsleepが先に実行されるので、5秒待機してからHello, World!が出るかと思いきや、すぐにHello,World!が出力され、5秒待機後にプロセスが終了します。
補足としてwait()を使わないと、sleepコマンドが終わる前にプログラムが終了しちゃいます。

waitは実行中のプロセスが終わるまで待機するメソッドです。


この記事が気に入ったらサポートをしてみませんか?