Pythonのサブプロセスとは何ですか? 【5つの使用例】

公開: 2021-05-23

サブプロセスを使用すると、オペレーティングシステムとまったく新しいレベルで対話できます。

私たちのコンピューターは常にサブプロセスを実行しています。 実際、この記事を読むだけで、ネットワークマネージャーやインターネットブラウザー自体のような多くのプロセスを実行していることになります。

これのすばらしい点は、コンピューターで実行するアクションには、サブプロセスの呼び出しが含まれることです。 Pythonで単純な「helloworld」スクリプトを作成している場合でも、これは当てはまります。

プログラミングをしばらく学んでいたとしても、サブプロセスの概念はあいまいに見えるかもしれません。 この記事では、サブプロセスの主な概念と、Pythonサブプロセス標準ライブラリの使用方法について詳しく説明します。

このチュートリアルの終わりまでに、次のことを行います。

  • サブプロセスの概念を理解する
  • Pythonサブプロセスライブラリの基本を学びました
  • 役立つ例を使ってPythonスキルを練習しました

入りましょう

サブプロセスの概念

大まかに言うと、サブプロセスは別のプロセスによって作成されたコンピュータープロセスです。

サブプロセスはツリーと考えることができます。このツリーでは、各親プロセスの背後で子プロセスが実行されています。 これはかなり混乱する可能性があることは知っていますが、簡単なグラフィックで見てみましょう。

コンピューターで実行されているプロセスを視覚化する方法はいくつかあります。 たとえば、UNIX(LinuxおよびMAC)には、インタラクティブなプロセスビューアであるhtopがあります。

Htopプロセスビューア

ツリーモードは、実行中のサブプロセスを確認するための最も便利なツールです。 F5で起動できます

コマンドセクションをよく見ると、コンピューターで実行されているプロセスの構造に気付くことができます。

Htopプロセス構造
それはすべて、コンピューター上の各プロセスを開始するコマンドである/ sbin / initで始まります。 その時点から、 xfce4-screenshoterxfce4-terminal (さらに多くのサブプロセスにつながる)のような他のプロセスの始まりを見ることができます

Windowsを見てみると、マシン上のクラッシュしたプログラムを強制終了するときに役立つ、神話上のタスクマネージャーがあります。

ウィンドウズタスクマネージャー

これで、明確な概念ができました。 Pythonでサブプロセスを実装する方法を見てみましょう。

Pythonのサブプロセス

Pythonのサブプロセスは、Pythonスクリプトがオペレーティングシステム(OS)に委任するタスクです。

サブプロセスライブラリを使用すると、Pythonから直接サブプロセスを実行および管理できます。 これには、標準入力stdin 、標準出力stdout 、および戻りコードの操作が含まれます。

Python標準ライブラリの一部であるため、PIPを使用してインストールする必要はありません。

したがって、モジュールをインポートするだけで、Pythonでサブプロセスの使用を開始できます。

 import subprocess # Using the module ....

注:この記事を続けるには、Python 3.5+が必要です。

現在使用しているPythonのバージョンを確認するには、を実行するだけです。

 ❯ python --version Python 3.9.5 # My result

取得するPythonのバージョンが2.xの場合、次のコマンドを使用できます

python3 --version

トピックを続けると、サブプロセスライブラリの背後にある主なアイデアは、Pythonインタープリターから直接、必要なコマンドを実行することでOSと対話できるようにすることです。

つまり、OSで許可されている限り(ルートファイルシステムを削除しない限り)、やりたいことが何でもできます。

現在のディレクトリのファイルを一覧表示する簡単なスクリプトを作成して、その使用方法を見てみましょう。

最初のサブプロセスアプリケーション

まず、ファイルlist_dir.pyを作成しましょう。 これは、リストファイルを実験するファイルになります。

 touch list_dir.py

それでは、そのファイルを開いて、次のコードを使用してみましょう。

 import subprocess subprocess.run('ls')

まず、サブプロセスモジュールをインポートし、次に実行する関数runを使用して、引数として渡すコマンドを実行します。

この関数は、subprocess.Popenへの使いやすいショートカットとしてPython3.5で導入されました。 subprocess.run関数を使用すると、コマンドを実行してコマンドが終了するのを待つことができます。これは、後で通信を呼び出すオプションがあるPopenとは対照的です。

コード出力についてlslsは現在のディレクトリのファイルを一覧表示するUNIXコマンドです。したがって、このコマンドを実行すると、現在のディレクトリに存在するファイルの一覧が表示されます。

 ❯ python list_dir.py example.py LICENSE list_dir.py README.md

注: Windowsを使用している場合は、別のコマンドを使用する必要があることに注意してください。 たとえば、 「ls」を使用する代わりに、 「dir」を使用できます

これは単純すぎるように思えるかもしれませんが、その通りです。 あなたはシェルがあなたにもたらすすべての力に完全なアプローチを取りたいと思っています。 それでは、サブプロセスを使用して引数をシェルに渡す方法を学びましょう。

たとえば、隠しファイル(ドットで始まるファイル)も一覧表示し、ファイルのすべてのメタデータも一覧表示するには、次のコードを記述します。

 import subprocess # subprocess.run('ls') # Simple command subprocess.run('ls -la', shell=True)

このコマンドを文字列として実行し、引数shellを使用しています。 つまり、サブプロセスの実行の開始時にシェルを呼び出しており、コマンド引数はシェルによって直接解釈されます。

ただし、use shell = Trueには多くの欠点があり、最悪の場合、セキュリティリークが発生する可能性があります。 それらについては、公式ドキュメントで読むことができます。

run関数にコマンドを渡す最良の方法は、リストを使用することです。ここで、 lst [0]は呼び出すコマンド(この場合はls)であり、 lst [n]はそのコマンドの引数です。

そうすると、コードは次のようになります。

 import subprocess # subprocess.run('ls') # Simple command # subprocess.run('ls -la', shell=True) # Dangerous command subprocess.run(['ls', '-la'])

サブプロセスの標準出力を変数に格納する場合は、引数capture_outputをtrueに設定することで保存できます。

 list_of_files = subprocess.run(['ls', '-la'], capture_output=True) print(list_of_files.stdout) ❯ python list_dir.py b'total 36\ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .\ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..\n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py\ndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git\n-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore\n-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py\n-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE\n-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.py\n-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md\n'

プロセスの出力にアクセスするには、インスタンス属性stdoutを使用します。

この場合、出力をバイトではなく文字列として格納する必要があります。これを行うには、text引数をtrueに設定します。

 list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True) print(list_of_files.stdout) ❯ python list_dir.py total 36 drwxr-xr-x 3 daniel daniel 4096 may 20 21:08 . drwx------ 30 daniel daniel 4096 may 20 18:03 .. -rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py drwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git -rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore -rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py -rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE -rw-r--r-- 1 daniel daniel 227 may 20 22:14 list_dir.py -rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md

これで、サブプロセスライブラリの基本がわかったので、次にいくつかの使用例に移ります。

Pythonでのサブプロセスの使用例

このセクションでは、サブプロセスライブラリの実際の使用法をいくつか確認します。 このGithubリポジトリでそれらすべてを確認できます。

プログラムチェッカー

このライブラリの主な用途の1つは、簡単なOS操作を行う機能です。

たとえば、プログラムがインストールされているかどうかを確認する簡単なスクリプト。 Linuxでは、 whichコマンドを使用してこれを行うことができます。

 '''Program checker with subprocess''' import subprocess program = 'git' process = subprocess. run(['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'The program "{program}" is installed') print(f'The location of the binary is: {process.stdout}') else: print(f'Sorry the {program} is not installed') print(process.stderr)

注: UNIXでは、コマンドが成功すると、そのステータスコードは0になります。それ以外の場合、実行中に問題が発生しました。

shell = True引数を使用していないため、ユーザー入力を安全に受け取ることができます。 また、入力が正規表現パターンを持つ有効なプログラムであるかどうかを確認できます。

 import subprocess import re programs = input('Separe the programs with a space: ').split() secure_pattern = '[\w\d]' for program in programs: if not re.match(secure_pattern, program): print("Sorry we can't check that program") continue process = subprocess. run( ['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'The program "{program}" is installed') print(f'The location of the binary is: {process.stdout}') else: print(f'Sorry the {program} is not installed') print(process.stderr) print('\n')

この場合、ユーザーからプログラムを取得し、プログラム文字列に文字と数字のみが含まれていることを証明する正規表現を使用しています。 forループで各プログラムの存在を確認します。

Pythonの単純なGrep

友人のトムは、テキストファイルと別の大きなファイルにパターンのリストを持っており、各パターンの一致数を取得したいと考えています。 彼はすべてのパターンに対してgrepコマンドを実行するのに何時間も費やしていました。

幸いなことに、あなたはPythonでこの問題を解決する方法を知っており、彼がこのタスクを数秒で達成するのを手伝ってくれるでしょう。

 import subprocess patterns_file = 'patterns.txt' readfile = 'romeo-full.txt' with open(patterns_file, 'r') as f: for pattern in f: pattern = pattern.strip() process = subprocess.run( ['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True) if int(process.stdout) == 0: print( f'The pattern "{pattern}" did not match any line of {readfile}') continue print(f'The pattern "{pattern}" matched {process.stdout.strip()} times')

このファイルを見て、使用するファイル名である2つの変数を定義します。 次に、すべてのパターンを含むファイルを開き、それらを繰り返し処理します。 次に、 「-c」フラグ(カウントを意味する)を指定してgrepコマンドを実行するサブプロセスを呼び出し、条件付きで一致の出力を決定します。

このファイルを実行する場合(Githubリポジトリからテキストファイルをダウンロードできることを忘れないでください)

サブプロセスを使用してvirtualenvを設定します

Pythonでできる最もクールなことの1つは、プロセスの自動化です。 この種のスクリプトを使用すると、1週間あたりの時間を節約できます。

たとえば、仮想環境を作成するセットアップスクリプトを作成し、現在のディレクトリでrequirements.txtファイルを見つけて、すべての依存関係をインストールしようとします。

 import subprocess from pathlib import Path VENV_NAME = '.venv' REQUIREMENTS = 'requirements.txt' process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True) if process1.returncode != 0: raise OSError('Sorry python3 is not installed') python_bin = process1.stdout.strip() print(f'Python found in: {python_bin}') process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True) shell_bin = process2.stdout.split('/')[-1] create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True) if create_venv.returncode == 0: print(f'Your venv {VENV_NAME} has been created') pip_bin = f'{VENV_NAME}/bin/pip3' if Path(REQUIREMENTS).exists(): print(f'Requirements file "{REQUIREMENTS}" found') print('Installing requirements') subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS]) print('Process completed! Now activate your environment with "source .venv/bin/activate"') else: print("No requirements specified ...")

この場合、複数のプロセスを使用し、Pythonスクリプトで必要なデータを解析しています。 また、 requirements.txtファイルが存在するかどうかを判断できるpathlibライブラリも使用しています。

Pythonファイルを実行すると、OSで何が起こっているかについてのいくつかの有用なメッセージが表示されます。

 ❯ python setup.py Python found in: /usr/bin/python3 Your venv .venv has been created Requirements file "requirements.txt" found Installing requirements Collecting asgiref==3.3.4 ....... Process completed! Now activate your environment with "source .venv/bin/activate"

標準出力を変数にリダイレクトしていないため、インストールプロセスから出力を取得することに注意してください。

別のプログラミング言語を実行する

Pythonで他のプログラミング言語を実行し、それらのファイルから出力を取得できます。 これが可能なのは、サブプロセスがオペレーティングシステムと直接相互作用するためです。

たとえば、C ++とJavaでHelloWorldプログラムを作成してみましょう。 次のファイルを実行するには、C ++およびJavaコンパイラをインストールする必要があります。

helloworld.cpp

 #include <iostream> int main(){ std::cout << "This is a hello world in C++" << std::endl; return 0; }


helloworld.java

 class HelloWorld{ public static void main(String args[]){ System.out.println("This is a hello world in Java"); } }


これは単純なPythonワンライナーと比較して多くのコードのように見えることを私は知っていますが、これはテスト目的のためだけです。

ディレクトリ内のすべてのC ++ファイルとJavaファイルを実行するPythonスクリプトを作成します。 これを最初に行うには、ファイル拡張子に応じてファイルのリストを取得する必要があります。globを使用すると、簡単に行うことができます。

 from glob import glob # Gets files with each extension java_files = glob('*.java') cpp_files = glob('*.cpp')

その後、サブプロセスを使用して各タイプのファイルを実行できるようになります。

 for file in cpp_files: process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' BTW this was runned by Python' print(output) for file in java_files: without_ext = file.strip('.java') process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' A Python subprocess runned this :)' print(output)

ちょっとしたコツは、文字列関数ストリップを使用して出力を変更し、必要なものだけを取得することです。

注:出力をメモリにロードしているため、メモリリークが発生する可能性があるため、大きなJavaまたはC ++ファイルを慎重に実行してください。

外部プログラムを開く

サブプロセスを介してバイナリの場所を呼び出すだけで、他のプログラムを実行できます。

私の好みのWebブラウザであるbraveを開いて試してみましょう。

 import subprocess subprocess.run('brave')

これにより、ブラウザインスタンスが開きます。すでにブラウザを実行している場合は、別のタブが開きます。

開いたブラウザ

フラグを受け入れる他のプログラムと同様に、フラグを使用して目的の動作を生成できます。

 import subprocess subprocess.run(['brave', '--incognito'])

シークレットフラグ

総括する

サブプロセスは、別のプロセスによって作成されたコンピュータープロセスです。 htopやタスクマネージャーなどのツールを使用して、コンピューターで実行されているプロセスを確認できます。

Pythonには、サブプロセスを操作するための独自のライブラリがあります。 現在、 run関数は、サブプロセスを作成および管理するためのシンプルなインターフェイスを提供します。

OSと直接やり取りしているので、それらを使用してあらゆる種類のアプリケーションを作成できます。

最後に、学ぶための最良の方法は、使用したいものを作成することであることを忘れないでください。