subprocessを使ってみる
コマンドをPythonコードで実行するには、どうすればいい?
インストール不要な標準モジュールのsubprocess
を使うと出来そうです。
os.system
などに変わってsubprocess
モジュールの使用を推奨されているようですので、それを使います。
目次
作業環境
Androidスマホ
・QPyNotebook
・Pydroid3でJupyterNotebook
・termuxでJupyterNotebook
実験
subprocessのメソッド
には
- run
- call
- check_call
- check_output
- Popen
と、幾つか種類があるようです。詳しいことは公式ページに任せるとして、とりあえず動かしてみます。
コマンドpwdでカレントディレクトリの取得
os.getcwd()を使えば取得できるが、subprocessモジュールの
subprocess.run
メソッドを使ってみる。
import subprocess subprocess.run("pwd")
CompletedProcess(args='pwd', returncode=0)
実行すればサクッとカレントディレクトリが表示されると思いきや、想像と別な表示がされた。
コマンドが正常に実行され終了すればreturncode=0
を返す。ですと。
subprocess.call
ではどうだ。
subprocess.call("pwd")
0
これも正常に実行と終了をした証拠
だけを残した。
subprocess.check_call
はどうでしょ。
subprocess.check_call("pwd")
0
またしても。
subprocess.check_output
では。
subprocess.check_output("pwd")
b'/storage/emulated/0/qpython/notebooks\n'
カレントディレクトリが表示された。
しかしバイナリ形式のb
になっている。
decode()
関数を使うとデコードできる。
res = subprocess.check_output("pwd") res.decode("utf-8")
'/storage/emulated/0/qpython/notebooks\n'
変換された。
subprocess.Popen
は飛ばす。
別のOSのコマンドを指定してみる
現在これをAndroidスマホで実行しているが、試しにWindowsコマンドのdir
を使うとどうなるか。
AndroidにWindowsのコマンドがないので実行できないとかエラーコードを吐くだろうけど、一応確認。
subprocess.check_output("dir")
PermissionErrorTraceback (most recent call last)
<ipython-input-7-aa07fb335683> in <module>()
----> 1 subprocess.check_output("dir")
~/lib/python36.zip/subprocess.py in check_output(timeout, *popenargs, **kwargs)
334
335 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
--> 336 **kwargs).stdout
337
338
~/lib/python36.zip/subprocess.py in run(input, timeout, check, *popenargs, **kwargs)
401 kwargs['stdin'] = PIPE
402
--> 403 with Popen(*popenargs, **kwargs) as process:
404 try:
405 stdout, stderr = process.communicate(input, timeout=timeout)
~/lib/python36.zip/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
707 c2pread, c2pwrite,
708 errread, errwrite,
--> 709 restore_signals, start_new_session)
710 except:
711 # Cleanup if the child failed starting.
~/lib/python36.zip/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
1342 if errno_num == errno.ENOENT:
1343 err_msg += ': ' + repr(err_filename)
-> 1344 raise child_exception_type(errno_num, err_msg, err_filename)
1345 raise child_exception_type(err_msg)
1346
PermissionError: [Errno 13] Permission denied: 'dir'
パーミッション・エラー。
逆にWindows上でLinuxコマンドなどをsubprocessで実行させようとしても同様のエラーが出るかな。
また、コマンドによってもシェルコマンドか、ファイルをダウンロードして使っている外部コマンドかで実行できなかったりするようです。
引数のあるコマンドの実行
コマンド コマンド引数
のように、引数を使う場合も同様にできるのか。
フォルダを新規作成してみる。
subprocess.run("mkdir Test_Dir")
PermissionErrorTraceback (most recent call last)
<ipython-input-31-80de53d19318> in <module>()
----> 1 subprocess.run("mkdir Test_Dir")
~/lib/python36.zip/subprocess.py in run(input, timeout, check, *popenargs, **kwargs)
401 kwargs['stdin'] = PIPE
402
--> 403 with Popen(*popenargs, **kwargs) as process:
404 try:
405 stdout, stderr = process.communicate(input, timeout=timeout)
~/lib/python36.zip/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
707 c2pread, c2pwrite,
708 errread, errwrite,
--> 709 restore_signals, start_new_session)
710 except:
711 # Cleanup if the child failed starting.
~/lib/python36.zip/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
1342 if errno_num == errno.ENOENT:
1343 err_msg += ': ' + repr(err_filename)
-> 1344 raise child_exception_type(errno_num, err_msg, err_filename)
1345 raise child_exception_type(err_msg)
1346
PermissionError: [Errno 13] Permission denied: 'mkdir Test_Dir'
パーミッションエラー。
コマンドをそのまま書いたら駄目っぽい。
リストで与えてやれば行けるようだ。
cmd = ["mkdir","Test_Dir"] subprocess.run(cmd)
CompletedProcess(args=['mkdir', 'Test_Dir'], returncode=0)
お、行けましたよ。
または
cmd = "mkdir フォルダ名" subprocess.run(cmd.split())
と、コマンドはそのまま書いて、split()
を使う。
作ったフォルダに移動してみる。
cmd2 = ["cd","Test_Dir"] subprocess.run(cmd2)
PermissionErrorTraceback (most recent call last)
<ipython-input-37-352d175499da> in <module>()
1 cmd2 = ["cd","Test_Dir"]
----> 2 subprocess.run(cmd2)
~/lib/python36.zip/subprocess.py in run(input, timeout, check, *popenargs, **kwargs)
401 kwargs['stdin'] = PIPE
402
--> 403 with Popen(*popenargs, **kwargs) as process:
404 try:
405 stdout, stderr = process.communicate(input, timeout=timeout)
~/lib/python36.zip/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
707 c2pread, c2pwrite,
708 errread, errwrite,
--> 709 restore_signals, start_new_session)
710 except:
711 # Cleanup if the child failed starting.
~/lib/python36.zip/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
1342 if errno_num == errno.ENOENT:
1343 err_msg += ': ' + repr(err_filename)
-> 1344 raise child_exception_type(errno_num, err_msg, err_filename)
1345 raise child_exception_type(err_msg)
1346
PermissionError: [Errno 13] Permission denied: 'cd'
パーミッション・エラー。
こちらによると、
shell=True
と書けばできる、と。
cmd2 = ["cd","Test_Dir"] subprocess.run(cmd2,shell=True)
CompletedProcess(args=['cd', 'Test_Dir'], returncode=0)
正常に実行終了した。
カレントディレクトリを見てみる。
subprocess.check_output("pwd")
b'/storage/emulated/0/qpython/notebooks\n'
移動していませんね。
上のQ&Aサイトの追記にあるように、元に戻ってしまう。
こちらはsubprocessではなく、os.chdir
で移動した場合。
stackoverflow スクリプトを終了すると元のディレクトリに戻る件
難易度高め。バッチ/シェルスクリプトを書いたりして対応している。
よし、一旦この件は放置。
ipynb形式をmd形式に変換
JupyterNotebookがインストールされていれば、jupyter nbconvert
のコマンドが使える筈なので、それをsubprocessで動かす。
上手く行けば、指定のipynbファイルがmdに変換され新規ファイルで保存される。
事前に用意したtest.ipynb
なるファイルを変換する。
変換後のファイル名は自動的に変換前ファイル名.md
のように拡張子のみ変わる。
import subprocess # mdファイルに変換したいipynbのファイル名 file = "test.ipynb" # コマンドの引数 args = ["jupyter", "nbconvert","--to","markdown"] # コマンド引数にipynbファイル名を追加 args.append(file) # コマンドの実行 try: subprocess.run(args) print("変換しました。") except: print("変換できませんでした。")
変換しました。
QPyNotebookで実行した。
「変換しました。」と言っているが、嘘。mdファイルは作られていなかった。
Pydroid3とtermuxからJupyter Notebookで実行したら変換できた。
以上。