cBlog

Tips for you.

Pythonでコマンドを実行しシェルスクリプトのように使う

f:id:cruller:20170819233109p:plain

一部修正しました(取り消し線部)

一連のコマンドを走らせたかったのですが、私はシェルスクリプトの文法が苦手です。そこで、Python 3でシェルのコマンドを実行させる方法を調べてみました。書きやすいし、より高級なことができると思います。

 

subprocessモジュール

他にも何かあるみたいですが、現在はsubprocessモジュールのrun()関数を用いるのが推奨されているようです。Python 3.5で追加されたほやほやの関数です。

以下のようにすると$ ls -l /usr/binを実行できます。

cmd = ['ls', '-l', '/usr/bin']
subprocess.run(cmd)

結果は標準出力に出力されます。結果をプログラム内で使いたい場合は引数にstdout=subprocess.PIPEを追加してください。run()の返り値はCompletedProcessクラスです。そのstdout属性にはバイト列として格納されているので適宜デコードしてください。以下は同じ出力がされます。

cmd = ['ls', '-l', '/usr/bin']
out = subprocess.run(cmd, stdout=subprocess.PIPE)
print(out.stdout.decode())

piyo of hoge.txtのようなスペースを含むファイル名を(コマンドライン)引数にとっても適切にエスケープクォートしてくれます。ただ、~(ホーム)や*のようなワイルドカードは(エスケープされるためか)もクォートされてしまい期待した通りいかないのでos.path.expanduser()glob.glob()を使えばいいと思います。

引数にshell=Trueをとるとそういった展開もやってくれますが危険なのでやめた方がいいらしいです(シェルインジェクション)。こわいこわい。

cmd = 'ls -l /usr/bin'
subprocess.run(cmd, shell=True)

(この場合は逆に、スペースやダメ文字メタ文字を含むファイル名はこちらでエスケープしてやる必要がありますが、)単一の文字列で渡せるのはいいなと思います。shell=Falseでは以下のように改行してやれば見やすい気がします。タイプは依然めんどくさいですけど。

cmd = [
    'command',
    '--hikisuu-nashi-option',
    '-o', 'hikisuu',
    ]

1行目に以下のshebangを書き、実行権限を付けてパスの通ったところにおけばオリジナルコマンドが作れますね。

#!/usr/bin/env python3

 

うん、これでbashの文法覚えずに済む。

 

参考になった記事

qiita.com

17.5. subprocess — サブプロセス管理 — Python 3.6.1 ドキュメント