run - subprocess python stderr




Pythonで外部コマンドを呼び出す (20)

Pythonスクリプト内から外部コマンドを呼び出すにはどうすればいいですか(UnixシェルまたはWindowsのコマンドプロンプトで入力したのと同じように)


Pythonで外部コマンドを呼び出す

Simple、 CompletedProcessオブジェクトを返すsubprocess.run使用します。

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

どうして?

Python 3.5以降、ドキュメントではsubprocess.runを推奨しています。

サブプロセスを呼び出すための推奨される方法は、処理できるすべてのユースケースに対してrun()関数を使用することです。 より高度なユースケースについては、基礎となるPopenインタフェースを直接使用することができます。

ここでは、最も簡単な使用例を示します。

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

runコマンドが正常に終了するまで待ってから、 CompletedProcessオブジェクトを返します。 代わりにTimeoutExpiredtimeout=引数を指定した場合)またはCalledProcessError (失敗し、 check=Trueを渡すcheck=True )を発生させることができます。

上の例から推測するように、stdoutとstderrはどちらもデフォルトで独自のstdoutとstderrにパイプされます。

返されたオブジェクトを検査し、与えられたコマンドとreturncodeを見ることができます:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

出力のキャプチャ

出力をキャプチャする場合は、 subprocess.PIPEを適切なstderrまたはstdout渡すことができます。

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(私は、バージョン情報がstdoutの代わりにstderrに置かれることは面白く、やや直観的ではないことがわかります)。

コマンドリストを渡す

プログラマチックに構築された文字列を提供することに、コマンド文字列(質問の示唆しているようなもの)を手動で提供することから簡単に移動することができます。 プログラムで文字列を構築しないでください。 これは潜在的なセキュリティ問題です。 あなたが入力を信頼しないと思う方が良いです。

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

注意すべきは、 argsのみが位置的に渡されるべきです。

フルシグネチャ

ソース内の実際の署名は、 help(run)示されていhelp(run)

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

popenargskwargsPopenコンストラクタに与えられます。 inputは、サブプロセスのstdinにパイプされるバイトの文字列(またはencodingを指定する場合はunicodeまたはuniversal_newlines=True )です。

ドキュメンテーションはtimeout=check=Trueを私ができるより良く説明しています:

timeout引数はPopen.communicate()に渡されます。 タイムアウトが経過すると、子プロセスは強制終了され、待機されます。 子プロセスが終了した後、TimeoutExpired例外が再発生します。

checkがtrueで、プロセスがゼロ以外の終了コードで終了した場合、CalledProcessError例外が送出されます。 その例外の属性は、引数、終了コード、およびstdoutとstderrが取得されている場合に保持されます。

check=Trueこの例は私が思い付くかもしれないものよりも優れています:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

拡張署名

ドキュメントに示されているように、拡張された署名があります:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

これは、argsリストだけが位置的に渡されるべきであることを示していることに注意してください。 残りの引数をキーワード引数として渡します。

ポッペン

Popen代わりに使うときは? 私は引数だけに基づいてユースケースを見つけるのに苦労します。 しかしPopenを直接使用すると、 poll 、 'send_signal'、 'terminate'、 'wait'などのメソッドにアクセスできます。

ソースで指定されたPopen署名があります 。 私はこれが情報の最も正確なカプセル化だと思う( help(Popen)とは対照的に):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

しかしPopenドキュメントはより有益です。

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

新しいプロセスで子プログラムを実行します。 POSIXでは、クラスはos.execvp()のような動作を使って子プログラムを実行します。 Windowsでは、クラスはWindowsのCreateProcess()関数を使用します。 Popenへの議論は以下の通りです。

Popenの残りのドキュメントを理解することは、読者のための練習として残されます。


更新:

あなたのコードが以前のPythonバージョンとの互換性を維持する必要がない場合、 subprocess.runPython 3.5の推奨アプローチです。 これは一貫性があり、Envoyと同様の使いやすさを提供します。 (配管はそれほど簡単ではありません。 どのようにこの質問を参照してください)。

ドキュメントのいくつかの例があります。

プロセスを実行します。

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

失敗した実行で発生:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

キャプチャ出力:

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

元の答え:

私はhttps://github.com/kennethreitz/envoy試みることをお勧めします。 これはサブプロセスのラッパーで、古いモジュールと関数を置き換えることを目指しています。 特使は人間のためのサブプロセスです。

readmeから使用例:

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

パイプのものも周り:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]

"pexpect" Pythonライブラリもチェックしてください。

ssh、ftp、telnetなどの外部プログラム/コマンドを対話的に制御することができます。

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

Popenを使用すると、プロシージャのステータスを確認できます。

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

subprocess.Popen確認してください。 subprocess.Popen




os.systemは結果を格納することを許可していないので、結果をいくつかのリストまたは何かに保管したい場合は、 subprocess.call機能します。


subprocess.check_callは、戻り値をテストしたくない場合に便利です。 エラーが発生した場合は例外がスローされます。


いくつかのヒントは、子プロセスを呼び出し元プロセスから切り離す(バックグラウンドで子プロセスを開始する)。

CGIスクリプトから長いタスクを開始したいとします。つまり、子プロセスはCGIスクリプト実行プロセスよりも長く存続する必要があります。

サブプロセスモジュールdocsの古典的な例は次のとおりです。

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

ここでの考え方は、longtask.pyが終了するまで、 'call subprocess'という行で待たされたくないということです。 しかし、この例の「ここではもう少しコードがある」という行の後に何が起こるのかははっきりしない。

私のターゲットプラットフォームはフリーズされていましたが、開発はウィンドウで行われていましたので、まずウィンドウの問題に直面しました。

ウィンドウ(win xp)では、longtask.pyが処理を終了するまで、親プロセスは終了しません。 あなたがCGIスクリプトで望むものではありません。 問題はPythonに固有ではなく、PHPコミュニティでも問題は同じです。

その解決策は、DETACHED_PROCESS プロセス作成フラグをwin APIの基礎となるCreateProcess関数に渡すことです。 pywin32をインストールした場合は、win32processモジュールからフラグをインポートできます。それ以外の場合は、自分で定義する必要があります。

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksunのコメントには、意味的に正しいフラグはCREATE_NEW_CONSOLE(0x00000010)* /

freebsdには別の問題があります。親プロセスが終了すると、子プロセスも終了します。 CGIスクリプトでもそうではありません。 いくつかの実験では、問題がsys.stdoutを共有するように見えることが示されました。 実際の解決策は次のとおりです。

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

私は他のプラットフォームでコードをチェックしておらず、freebsdの動作の理由を知らない。 誰もが知っている場合は、あなたのアイデアを共有してください。 Pythonでバックグラウンドプロセスを開始しても、まだ光るものはありません。


ここではこれまでに言及していない別の違いがあります。

subprocess.Popenは<command>をサブプロセスとして実行します。 私の場合、別のプログラム<b>と通信する必要があるファイル<a>を実行する必要があります。

私はサブプロセスを試して、実行は成功しました。 しかし、<b>は<a>と通信できませんでした。 ターミナルから両方を実行するとすべて正常です。

もう1つ:(注:kwriteは他のアプリケーションとは異なる動作をしますが、Firefoxで以下のように試してみると結果は変わりません)。

os.system("kwrite")を試してみると、ユーザーがkwriteを閉じるまでプログラムフローがフリーズします。 それを克服するために、私は代わりにos.system(konsole -e kwrite) 。 この時間プログラムは流れ続けましたが、kwriteはコンソールのサブプロセスになりました。

誰もがサブプロセスではないkwriteを実行します(つまり、システムモニタではツリーの一番左端に表示する必要があります)。


これは単純なことができます:

import os
cmd = "your command"
os.system(cmd)

つかいます:

import os

cmd = 'ls -al'

os.system(cmd)

os - このモジュールは、オペレーティングシステムに依存する機能を移植可能な方法で提供します。

より多くの関数については、 hereにドキュメントがあります。


呼び出しているコマンドの出力が必要な場合は、 subprocess.check_output (Python 2.7+)を使用できます。

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

shellパラメータにも注意してください。

shellがTrue場合、指定されたコマンドはシェルを通して実行されます。 これは、ほとんどのシステムシェルで提供される拡張制御フローのためにPythonを主に使用していて、シェルパイプ、ファイル名ワイルドカード、環境変数展開、ユーザーの自宅への展開などの他のシェル機能への便利なアクセスを必要とする場合に役立ちますディレクトリ。 しかし、Python自体は多くのシェルのような機能(特にglobfnmatchos.walk()os.path.expandvars()os.path.expanduser() 、およびshutil )の実装を提供していることに注意してください。


外部プログラムを呼び出す方法とそれぞれの長所と短所を要約します。

  1. os.system("some_command with args")はコマンドと引数をシステムのシェルに渡します。 この方法で一度に複数のコマンドを実行し、パイプと入出力リダイレクションを設定できるので、これはうれしいことです。 例えば:

    os.system("some_command < input_file | another_command > output_file")  
    

    しかし、これは便利ですが、スペースなどのシェル文字のエスケープを手動で処理する必要があります。一方、これはまた、実際には外部プログラムではなくシェルコマンドであるコマンドを実行することもできます。 ドキュメントを参照してください。

  2. stream = os.popen("some_command with args")は、 os.systemと同じことを行いますが、プロセスの標準入出力にアクセスするためのファイル形式のオブジェクトを提供します。 すべてのI / Oをやや異なった方法で扱うpopenの3つの変種があります。 すべてを文字列として渡すと、コマンドはシェルに渡されます。 それらをリストとして渡すと、何かをエスケープする心配はありません。 ドキュメントを参照してください。

  3. subprocessモジュールのPopenクラス。 これはos.popen置き換えることを意図していますが、非常に包括的であるために少し複雑になるという欠点があります。 たとえば、次のようにします。

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    の代わりに:

    print os.popen("echo Hello World").read()
    

    4つの異なるpopen関数の代わりに1つの統一されたクラスにすべてのオプションを持つことは良いことです。 ドキュメントを参照してください。

  4. subprocessモジュールからのcall関数。 これは基本的にPopenクラスと似ていますが、同じ引数をすべてとりますが、コマンドが完了してリターンコードを返すまで待っています。 例えば:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    ドキュメントを参照してください。

  5. Python 3.5以降では、新しいsubprocess.run関数を使用することができます。この関数は、上記のような多くの柔軟性を持ち、コマンドの実行がCompletedProcessするとCompletedProcessオブジェクトを返します。

  6. osモジュールには、Cプログラムに含まれるすべてのfork / exec / spawn関数もありますが、直接使用することはお勧めしません。

subprocessモジュールはおそらくあなたが使用するものでなければなりません。

最後に、シェルが実行する最終コマンドを文字列として渡すすべてのメソッドで、エスケープする必要があることに注意してください。 渡す文字列の一部が完全に信頼できない場合は、深刻なセキュリティ上の問題があります。 たとえば、ユーザーが文字列の一部または全部を入力している場合などです。 不明な場合は、これらのメソッドを定数でのみ使用してください。 あなたには、このコードを考慮する意味を示唆するために:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

ユーザーが「私のママは私を愛していません&&rm -rf /」と入力すると想像してください。


標準ライブラリのサブプロセスモジュールを見てください:

from subprocess import call
call(["ls", "-l"])

サブプロセスシステムの利点は、柔軟性が高いことです(stdout、stderr、 "実際の"ステータスコード、エラー処理の改善など)。

公式文書では、代替のos.system()を介してサブプロセス・モジュールを推奨しています。

サブプロセスモジュールは、新しいプロセスを生成してその結果を取得するための、より強力な機能を提供します。 そのモジュールを使用する方がこの関数[ os.system() ]を使用する方が望ましいです。

サブプロセスのドキュメントの「 古い関数をサブプロセスモジュールに置き換える 」には、役立つレシピがいくつかあります。

サブプロセスモジュールの公式文書


私はshlex一緒にサブプロセスを使う傾向があります(引用符で囲まれた文字列をエスケープするため)。

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

私はそのシンプルさのためにshell_commandです。 これはサブプロセスモジュールの上に構築されています。

ドキュメントの例を次に示します。

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

私は通常、

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

パイプ内のstdoutデータを使用して自由に行うことができます。 実際には、これらのパラメータ( stdout=stderr= )を省略するだけで、 os.system()ように動作します。


Linuxでは、独立して実行される外部コマンドを呼び出す場合(Pythonスクリプトが終了した後も実行される)、タスクスプーラまたはatコマンドとして単純なキューを使用できます

タスクスプーラの例:

import os
os.system('ts <your-command>')

タスクスプーラ(ts)についての注意:

  1. 実行する同時プロセス数( "スロット")を次のように設定することができます。

    ts -S <number-of-slots>

  2. インストールにtsは管理者特権が必要ありません。ソースから簡単makeにダウンロードしてコンパイルすることができます。パスに追加すると完了です。


import os
cmd = 'ls -al'
os.system(cmd)

コマンドの結果を返す場合は、 os.popenを使用できます。 しかし、これはバージョン2.6以降では非推奨になっています。





external