PythonからLinuxコマンドを実行して戻り値を取得する方法

方法1:osモジュールのsystemメソッドを使用する

os.system(cmd)は、**ステータスコードのみを返します。** os.system(cmd)は子プロセスを起動し、その子プロセス内でcmdを実行します。cmdに実行内容がある場合、標準出力に表示されます。

この呼び出しは非常に直接的で、同期処理が行われます。**プログラムはブロックして待機し、戻り値を待機する必要があります。** 戻り値はシステムに依存し、システムの呼び出し戻り値を直接返すため、WindowsとLinuxでは異なります。

Linuxでは、その戻り値は**シェルコマンド実行後のステータスコード**で、int型です。0はシェルコマンドが正常に実行されたことを示し、256/512は見つからないことを示します。**このメソッドは、シェルコマンドの出力内容が必要ないシナリオに適しています。**

例を示します:

# 現在のディレクトリ内のすべてのファイルをリスト表示
import os
val = os.system('ls -al')
print(val)  # 出力は0
val = os.system('ls -al non_exist_folder')
print(val)  # 出力は512
val = os.system('ls -al|grep non_exist_val')
print(val)  # 出力は256

方法2:os.popen()を使用する

**情報のみを返し、** このメソッドは**シェルコマンド実行後の結果をファイル形式で返します。** **内容を取得するには、read()またはreadlines()メソッドを使用できます。**

os.popen(command[,mode[,bufsize])

  • mode – モード権限は 'r'(デフォルト) または 'w' にできます。
  • bufsize – ファイルが必要とするバッファサイズを指定します
  • 0はバッファなしを意味します;1は行バッファを意味します;
  • その他の正の値は、パラメータサイズのバッファを使用することを示します(概算値、バイト単位)。
  • 負のbufsizeはシステムのデフォルト値を使用することを意味します。一般的に、ttyデバイスの場合、それは行バッファです;その他のファイルの場合、それは全バッファです。このパラメータがない場合、システムのデフォルト値を使用します。

os.popenは非同期で実行され、**cmdコマンドの実行が完了するのを待たずに次のコードに進みますが、** コマンド実行結果を取得するために**read()またはreadlines()を使用すると、ブロック効果が発生します。** しかし、コマンド実行が終了できない場合やインタラクティブモードに入った場合、この「読み取り」は完全なブロック状態になります。

**popenメソッドはp.read()を通じて端末出力を取得し、** また**popenはclose()で閉じる必要があります。** 実行が成功した場合、close()は何も返しません。**失敗した場合、close()はシステムの戻り値を返します。** したがって、os.systemとは異なる方法で戻り値を取得します。

例:

>>> import os
>>> p = os.popen('ls ./')
>>> p.read()    # read()を使用して文字列を取得
'generate_charts_by_testIds_610.py\ngenerate_charts_by_testIds_modify.py\npsnr_bitrate.pdf\nssim_bitrate.pdf\n'
>>> p.read()    # 2回目の読み取りは空
''
>>> p = os.popen('ls ./')
>>> p.readlines()   # readlines()を使用してリストを取得
['generate_charts_by_testIds_610.py\n', 'generate_charts_by_testIds_modify.py\n', 'psnr_bitrate.pdf\n', 'ssim_bitrate.pdf\n']
>>>
>>> p     # popenはファイルオブジェクトを返すため、使用後は閉じる必要があります
<open file 'ls ./', mode 'r' at 0x7f16581afd20>    
>>> p.close()
>>>
>>> p
<closed file 'ls ./', mode 'r' at 0x7f16581afd20>
>>>
>>> with os.popen('ls ./') as aa:    # withステートメントを使用すれば、明示的にp.close()を書く必要はありません
...     r = aa.read()
...
>>> aa
<closed file 'ls ./', mode 'r' at 0x7f16581afc90>
>>>
>>> r
'generate_charts_by_testIds_610.py\ngenerate_charts_by_testIds_modify.py\npsnr_bitrate.pdf\nssim_bitrate.pdf\n'
>>>
>>> val = os.popen('ls -al')
>>> for i in val.readlines():
...     print(i)

方法3:commandsモジュールを使用する(Python3.0で削除)

3つのメソッドが使用できます:

(1)commands.getstatusoutput(cmd):コマンド実行後の戻り状態と実行結果をタプル(status,output)の形式で返します。cmdの実行は実際には{cmd;} **2>&1**の方法で行われるため、**outputにはコンソール出力情報またはエラー情報が含まれ、** outputには末尾の改行が含まれません。

(2)commands.getoutput(cmd):cmdの出力結果を返します。

(3)commands.getstatus(file):**ls -l fileの実行結果文字列を返します。** getoutputを呼び出し、このメソッドの使用は推奨されません。

import commands
aa = commands.getstatusoutput('ls -l')
# 出力:(0, 'total 0\n-rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 a.txt\n-rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 b.txt') 
# タプルを返し、os.popen(cmd)の戻り結果と比較すると、outputには末尾の改行が含まれていないことがわかります
bb = commands.getoutput('ls -l')  # 実行結果のみを返す
# 出力:'total 0\n-rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 a.txt\n-rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 b.txt'
cc = commands.getstatus('a.txt')  # 特定のファイルの状態を照会
# 出力:'-rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 a.txt'
dd = commands.getstatusoutput('ls -l non_exist_folder')  # 実行結果にはコンソール出力情報またはエラー情報が含まれます
# 出力:(512, "ls: cannot access 'non_exist_folder': No such file or directory")

os.popen(cmd)はファイルのようなオブジェクトを返し、commands.getoutput(cmd)は文字列型の出力結果を返します。多くの場合、より便利に使用できます。

方法4:subprocessモジュールを使用する

多くの子プロセスを作成でき、作成時に子プロセスと子プロセスの入力、出力、エラー出力パイプを指定でき、実行後に出力結果と実行状態を取得できます。

(1)**subprocess.call()**:指定されたコマンドを実行し、コマンド実行状態を返します。0または0以外の値で、**os.system(cmd)と機能が似ています。**

>>> res = subprocess.call(["ls", "-l"])
合計 28
-rw-r--r-- 1 root root   0 6月 16 10:28 1
drwxr-xr-x 2 root root 4096 6月 22 17:48 _1748
-rw-------. 1 root root 1264 4月 28 20:51 anaconda-ks.cfg
drwxr-xr-x 2 root root 4096 5月 25 14:45 monitor
-rw-r--r-- 1 root root 13160 5月  9 13:36 npm-debug.log

>>> res   # コマンド実行状態
0

(2)**subprocess.check_call()**:**python2.5**で新しく追加された関数で、指定されたコマンドを実行します。実行が成功すればステータスコードを返し、そうでなければ**例外をスローし、try…except…でチェックできます。**

>>> ret = subprocess.check_call(["ls", "-l", "vpu_deamon.log"])
-rw-rw-r-- 1 ubuser ubuser 8784 Nov 11  2020 vpu_deamon.log
>>> ret
0
>>>
>>> ret = subprocess.check_call(["ls", "-l", ".log"])
ls: cannot access '.log': No such file or directory
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 541, in check_call
    raise<strong> CalledProcessError</strong>(retcode, cmd)
subprocess.<strong>CalledProcessError</strong>: Command '['ls', '-l', '.log']' returned non-zero exit status 2

(3)**subprocess.check_output()**:文字列形式のコマンドを受け取り、実行結果を返します。Python 2では文字列を返し、Python 3ではバイト形式の結果を返します。失敗した場合は**例外をスローし、try…except…でチェックできます。**

#python 2.7
>>> res = subprocess.check_output("pwd")
>>> res
'/home/ubuser\n'
>>> type(res)
<type 'str'>

#python 3.5.2
>>> res = subprocess.check_output("pwd")
>>> res
b'/home/ubuser\n'
>>> type(res)
<class 'bytes'>

(4)**subprocess.run()**:**python3.5**で新しく追加された関数で、指定されたコマンドを実行し、コマンドの実行が完了するのを待って、実行結果を含むCompletedProcessクラスのインスタンスを返します。

>>> import subprocess
>>> subprocess.run(["df", "-h"]) # Pythonが解析する場合は、コマンドの各パラメータのリストを渡します
Filesystem      Size Used Avail Use% Mounted on
/dev/mapper/VolGroup-LogVol00
           289G  70G 204G 26% /
tmpfs         64G   0  64G  0% /dev/shm
/dev/sda1       283M  27M 241M 11% /boot
<strong>CompletedProcess</strong>(args=['df', '-h'], returncode=0)

>>> subprocess.run("df -h|grep /dev/sda1", shell=True)  # Linuxシェルに解析させる場合は、コマンド文字列を渡し、shell=Trueにします
/dev/sda1       283M  27M 241M 11% /boot
<strong>CompletedProcess</strong>(args='df -h|grep /dev/sda1', returncode=0)

(4)**subprocess.getstatusoutput()**:**python3で使用可能で、** 文字列形式のコマンドを受け取り、タプル形式の結果を返します。最初の要素はコマンド実行状態で、2番目は実行結果です。

>>> subprocess.getstatusoutput('pwd')
(0, '/root')

>>> subprocess.getstatusoutput('pd')
(127, '/bin/sh: pd: command not found')

(5)**subprocess.getoutput()**:**python3で使用可能で、** 文字列形式のコマンドを受け取り、実行結果を返します。

>>> subprocess.getoutput('pwd')
'/root'

(7)**subprocess.Popen()**:上記のsubprocessメソッドはすべて、subprocess.Popenのラッパーです。

class Popen(args, bufsize=0, executable=None, stdin=None, **stdout**=None, **stderr**=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)、Popenオブジェクトが作成されると、**メインプログラムは自動的に子プロセスの完了を待ちません。** wait()メソッドを呼び出す必要があり、親プロセスは待機します(つまりブロックします)。

1)stdout =subprocess.PIPE と stderr=subprocess.PIPE 、Python 2では文字列を返し、Python 3ではバイト形式の結果を返します。

#python 3
>>> res = subprocess.Popen("ls /tmp/", shell=True, stdout=subprocess.PIPE) # パイプを使用
>>> res.stdout.read()  # 標準出力
b'/tmp/yum.log\n'
res.stdout.close()  # 閉じる
>>> 
>>> res = subprocess.Popen("lm -l", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 標準出力は空
>>> res.stdout.read()
b'' # 標準エラーにはエラー情報があります
>>> res.stderr.read()
b'/bin/sh: lm: command not found\n'

2)poll() コマンドが実行完了したかどうかを定期的にチェックし、実行完了後は実行結果の状態を返し、完了していない場合はNoneを返します。

3)wait() コマンドの実行完了を待ち、結果状態を返します。

4)terminate() プロセスを終了します。

5)pid 現在実行中の子シェルのプロセスIDを取得します。

説明:subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, **timeout**=None, check=False, universal_newlines=False)

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, **timeout**=None)

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, **timeout**=None)

args:シェルコマンドを示します。文字列形式でシェルコマンドを与える場合、例えば"ls -l "の場合はshell = Trueにする必要があります。それ以外の場合はデフォルトで配列形式でシェル変数を示します。例えば["ls", "-l"]です。

比較的複雑なシェルステートメントを使用する場合、まずshlexモジュールのshlex.split()メソッドを使用してコマンドをフォーマットし、次にrun()メソッドまたはPopenに渡すことができます。

subprocess.run()、subprocess.call()、subprocess.check_call()、subprocess.check_output()はすべてsubprocess.Popenをラップして実装された高レベル関数であるため、より複雑な機能が必要な場合は、subprocess.Popenを通じて実現できます。

使用例:

import shlex
import subprocess
command = 'ls -l'
args = shlex.split('ls -l')
bb = subprocess.Popen(args) # bbはPopenオブジェクトで、read()またはreadlines()で読み取ることはできません
# 出力:合計 0
# -rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 a.txt
# -rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 b.txt # ここで改行をクリックする必要があります
aa = subprocess.call('ls -l', shell=True)  # 戻り値はシェルコマンドの実行戻りコードです
# 出力:合計 0
# -rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 a.txt
# -rw-rw-r-- 1 roaddb roaddb 0 Dec 11 10:09 b.txt # 直接戻ります

Python3での状況

期待:Python3でcurlコマンドを実行し、実行結果を返し、戻り値はjson形式です。

分析:

cmd='curl -s "http://127.0.0.1:8080/xxxxx"'

1) os.system(cmd)を使用すると出力内容を取得できません

2) os.popen(cmd).read()を使用すると、戻されたjson形式${aa}を取得でき、json.loads(${aa})を使用してjson形式を辞書形式に変換できます

3) commandsモジュールはPython3.0で廃止されました

4) **subprocess.getstatusoutput**(cmd)の出力結果は、Python2のcommands.getstatusoutput(cmd)と似ており、タプルを返し、タプルの2番目の値は実行結果${aa}で、json.loads(${aa})を使用してjson形式を辞書形式に変換できます。

タグ: Python Linuxコマンド subprocess osモジュール シェルスクリプト

7月15日 23:10 投稿