私のローカルマシンで、私はこの行を含むpythonスクリプトを実行します。
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
これはうまくいきます。
それから私はサーバー上で同じコードを実行し、私は次のエラーメッセージが表示されます
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
そこで私がしたのは、os.system()
を付けて実行する前に、端末のコマンドよりも私を表示するprint bashCommand
を挿入することです。
もちろん、私は再びエラーを受け取ります(os.system(bashCommand)
が原因で)、そのエラーの前には端末にコマンドを表示します。それから私はちょうどその出力をコピーして端末にコピーペーストをしてエンターを押すとそれは動作します...
何が起こっているのかについての手がかりがありますか?
os.system
を使用しないでください。それは サブプロセス を支持して非推奨になりました。 のドキュメントから : "このモジュールはいくつかの古いモジュールや関数を置き換えようとしています:os.system
、os.spawn
"。
あなたの場合のように:
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
ここで以前の回答を多少拡張するために、一般的に見落とされている多くの詳細があります。
subprocess.run()
よりもsubprocess.check_call()
を、subprocess.call()
よりもsubprocess.Popen()
よりもos.system()
よりもos.popen()
を優先しますtext=True
、別名universal_newlines=True
を理解し、おそらく使用してください。Shell=True
またはShell=False
の意味と、それがクォートを変更する方法とシェルの便利さを理解します。sh
とBashの違いを理解するこれらのトピックについては、以下でさらに詳しく説明します。
subprocess.run()
またはsubprocess.check_call()
を優先subprocess.Popen()
関数は低レベルの主力ツールですが、正しく使用するのは難しいため、複数行のコードをコピー/貼り付けてしまうことになります。 、以下で詳細に説明します。
ドキュメンテーション の段落を次に示します。
サブプロセスを呼び出すための推奨されるアプローチは、処理可能なすべてのユースケースで
run()
関数を使用することです。より高度な使用例については、基になるPopen
インターフェイスを直接使用できます。
残念ながら、これらのラッパー関数の可用性はPythonバージョン間で異なります。
subprocess.run()
はPython 3.5で公式に導入されました。以下のすべてを置き換えることを意図しています。subprocess.check_output()
はPython 2.7/3.1で導入されました。基本的にsubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
と同等ですsubprocess.check_call()
はPython 2.5で導入されました。基本的にsubprocess.run(..., check=True)
と同等ですsubprocess.call()
は、元のsubprocess
モジュール( PEP-324 )のPython 2.4で導入されました。基本的にsubprocess.run(...).returncode
と同等ですsubprocess.Popen()
リファクタリングおよび拡張されたsubprocess.run()
は、置き換えられる古いレガシー関数よりも論理的で汎用性があります。 CompletedProcess
オブジェクトを返します。このオブジェクトには、終了ステータス、標準出力、および完了したサブプロセスから他のいくつかの結果とステータスインジケータを取得できるさまざまなメソッドがあります。
subprocess.run()
は、単にプログラムを実行してPythonに制御を返す必要がある場合に使用する方法です。より複雑なシナリオ(バックグラウンドプロセス、おそらくPython親プログラムとの対話型I/Oを使用)では、subprocess.Popen()
を使用して、すべての配管作業を行う必要があります。これには、すべての可動部品のかなり複雑な理解が必要であり、軽く行うべきではありません。より単純な Popen
オブジェクト は、サブプロセスの残りのライフタイムの間、コードから管理する必要がある(おそらく実行中の)プロセスを表します。
おそらくsubprocess.Popen()
だけがプロセスを作成することを強調する必要があります。そのままにしておくと、Pythonと並行してサブプロセスが同時に実行されるため、「バックグラウンド」プロセスになります。入力や出力を行う必要がない場合、または他の方法で調整する必要がない場合は、Pythonプログラムと並行して有用な作業を行うことができます。
os.system()
とos.popen()
を避ける永遠から(まあ、Python 2.5以降) os
モジュールドキュメント には、os.system()
よりもsubprocess
を優先する推奨事項が含まれています。
subprocess
モジュールは、新しいプロセスを生成し、その結果を取得するためのより強力な機能を提供します。このモジュールを使用する方が、この関数を使用するよりも望ましいです。
system()
の問題は、明らかにシステム依存であり、サブプロセスと対話する方法を提供しないことです。 Pythonの手の届かない範囲で標準出力と標準エラーで実行されます。 Pythonが受け取る唯一の情報は、コマンドの終了ステータスです(ゼロは成功を意味しますが、ゼロ以外の値の意味もある程度システムに依存します)。
PEP-324 (既に上記で説明した)には、os.system
に問題がある理由と、subprocess
がそれらの問題を解決しようとする方法のより詳細な理論的根拠が含まれています。
os.popen()
は以前よりも 強く推奨されていません :
バージョン2.6から非推奨:この関数は廃止されました。
subprocess
モジュールを使用します。
ただし、Python 3のいつかで、subprocess
を使用するように再実装され、詳細についてはsubprocess.Popen()
ドキュメントにリダイレクトされます。
check=True
を使用しますまた、subprocess.call()
にはos.system()
と同じ制限の多くがあることに気付くでしょう。通常の使用では、通常、プロセスが正常に終了したかどうかを確認する必要があります。これはsubprocess.check_call()
およびsubprocess.check_output()
が実行します(後者は終了したサブプロセスの標準出力も返します)。同様に、サブプロセスがエラーステータスを返すことを特に許可する必要がない限り、通常、check=True
をsubprocess.run()
とともに使用する必要があります。
実際には、check=True
またはsubprocess.check_*
を使用すると、サブプロセスがゼロ以外の終了ステータスを返す場合、Pythonは CalledProcessError
例外 をスローします。
subprocess.run()
の一般的なエラーは、check=True
を省略し、サブプロセスが失敗した場合にダウンストリームコードが失敗したときに驚くことです。
一方、check_call()
とcheck_output()
の一般的な問題は、これらの関数を盲目的に使用したユーザーが例外が発生したときに驚いたことです。 grep
が一致を見つけられなかったとき。 (おそらく、grep
をネイティブPythonコードに置き換える必要があります。以下に概説します。)
すべてを数え、シェルコマンドが終了コードを返す方法と、どのような条件下でゼロ以外(エラー)の終了コードを返すかを理解し、それをどのように処理するかを意識的に決定する必要があります。
text=True
別名universal_newlines=True
を理解して使用してくださいPython 3以降、Pythonの内部の文字列はUnicode文字列です。ただし、サブプロセスがUnicode出力または文字列を生成するという保証はありません。
(違いがすぐに明らかにならない場合は、Ned Batchelderの Pragmatic Unicode をお勧めします。完全に必須ではありませんが、読むことをお勧めします。大幅に短縮されました。)
Pythonは、bytes
バッファーをフェッチして、何らかの方法で解釈する必要があります。バイナリデータのblobが含まれている場合、をUnicode文字列にデコードするべきではありません。エンコードされたテキストとバイナリデータを適切に区別する方法が存在する前に、多くのPython 2スクリプトを混乱させた厄介な動作の一種。
text=True
を使用すると、Pythonに、実際にはシステムのデフォルトエンコーディングでテキストデータを戻すことを期待し、Python(Unicode)文字列に最適にデコードする必要があることを伝えます。 Pythonの能力(通常は、Windowsを除く、適度に最新のシステムではUTF-8ですか?)
それがnotではない場合、Pythonは、bytes
およびstdout
文字列内のstderr
文字列を提供します。たぶん、後でdoがテキスト文字列であることを知っていて、エンコーディングを知っているかもしれません。その後、それらをデコードできます。
normal = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True,
text=True)
print(normal.stdout)
convoluted = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))
Python 3.7では、以前は多少誤解を招くようなuniversal_newlines
と呼ばれていたキーワード引数に、より短く、説明的で理解しやすいエイリアスtext
が導入されました。
Shell=True
対Shell=False
を理解するShell=True
を使用すると、1つの文字列をシェルに渡すと、シェルはそれをそこから取得します。
Shell=False
を使用すると、引数のリストをOSに渡し、シェルをバイパスします。
シェルがない場合は、プロセスを保存し、 かなりの量の隠された複雑さを取り除きます。これは、バグやセキュリティ問題さえも隠さない場合もあります。
一方、シェルがない場合、リダイレクト、ワイルドカード拡張、ジョブ制御、およびその他の多数のシェル機能はありません。
よくある間違いは、Shell=True
を使用した後、Pythonトークンのリストを渡すこと、またはその逆です。これは場合によっては機能しますが、実際には不明確であり、興味深い方法で破壊される可能性があります。
# XXX AVOID THIS BUG
buggy = subprocess.run('Dig +short stackoverflow.com')
# XXX AVOID THIS BUG TOO
broken = subprocess.run(['Dig', '+short', 'stackoverflow.com'],
Shell=True)
# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['Dig +short stackoverflow.com'],
Shell=True)
correct = subprocess.run(['Dig', '+short', 'stackoverflow.com'],
# Probably don't forget these, too
check=True, text=True)
# XXX Probably better avoid Shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('Dig +short stackoverflow.com',
Shell=True,
# Probably don't forget these, too
check=True, text=True)
一般的なレトルト「しかし、それは私のために機能します」は、どのような状況で機能しなくなる可能性があるかを正確に理解しない限り、有用な反論ではありません。
多くの場合、シェルの機能はネイティブPythonコードで置き換えることができます。単純なAwkまたはsed
スクリプトは、おそらく代わりにPythonに単純に変換する必要があります。
これを部分的に説明するために、ここに多くのシェル機能を含む典型的ではあるが少しばかげた例を示します。
cmd = '''while read -r x;
do ping -c 3 "$x" | grep 'round-trip min/avg/max'
done <hosts.txt'''
# Trivial but horrible
results = subprocess.run(
cmd, Shell=True, universal_newlines=True, check=True)
print(results.stdout)
# Reimplement with Shell=False
with open('hosts.txt') as hosts:
for Host in hosts:
Host = Host.rstrip('\n') # drop newline
ping = subprocess.run(
['ping', '-c', '3', Host],
text=True,
stdout=subprocess.PIPE,
check=True)
for line in ping.stdout.split('\n'):
if 'round-trip min/avg/max' in line:
print('{}: {}'.format(Host, line))
ここで注意すべき点がいくつかあります。
Shell=False
を使用すると、文字列の周りにシェルが必要とする引用符は必要ありません。とにかく引用符を付けることはおそらくエラーです。また、リファクタリングされたコードは、非常に簡潔な構文を使用して、シェルが実際にどれだけ機能するかを示しています-良くも悪くも。 Pythonはexplicitはimplicitより優れていますが、Pythonコード isむしろ冗長で、間違いなくこれは実際よりも複雑に見えます。一方、シェルコマンドの出力と共にホスト名を簡単に含めることができるようになった拡張機能によって簡単に例示されるように、他の何かの途中でコントロールを取得できるポイントがいくつかあります。 (これは、シェルで行うことも決して困難ではありませんが、さらに別の転換とおそらく別のプロセスを犠牲にして行われます。)
完全を期すために、これらのシェル機能のいくつかの簡単な説明と、それらがネイティブPython機能に置き換えられる可能性のある方法についてのいくつかの注記を示します。
glob.glob()
で置き換えることができます。または、for file in os.listdir('.'): if not file.endswith('.png'): continue
のような単純なPython文字列比較で置き換えることもできます。 Bashには、.{png,jpg}
波括弧展開、{1..100}
、チルダ展開などのさまざまな拡張機能があります(~
はホームディレクトリに、より一般的には~account
は別のユーザーのホームディレクトリに展開します)grep 'foo' <inputfile >outputfile
は、書き込み用にoutputfile
を開き、読み取り用にinputfile
を開き、その内容を標準入力としてgrep
に渡し、その標準出力はoutputfile
に到達します。これは通常、ネイティブPythonコードに置き換えるのは難しくありません。echo foo | nl
は2つのサブプロセスを実行します。ここで、echo
の標準出力はnl
の標準入力です(OSレベルでは、Unixライクシステムでは、これは単一のファイルハンドルです)。パイプラインの一端または両端をネイティブPythonコードで置き換えることができない場合は、おそらくパイプラインに2つまたは3つ以上のプロセスがある場合(特に pipes
を見てください) Python標準ライブラリ のモジュール、またはより現代的で汎用性の高い多数のサードパーティの競合製品)。sh
とBashの違いを理解するsubprocess
は、特に要求しない限り、/bin/sh
を使用してシェルコマンドを実行します(Windowsではもちろん、COMSPEC
変数の値を使用します)。これは、 配列、[[
など などのさまざまなBash専用機能が使用できないことを意味します。
Bashのみの構文を使用する必要がある場合は、executable='/bin/bash'
としてシェルにパスを渡すことができます(もちろん、Bashが別の場所にインストールされている場合は、パスを調整する必要があります)。
subprocess.run('''
# This for loop syntax is Bash only
for((i=1;i<=$#;i++)); do
# Arrays are Bash-only
array[i]+=123
done''',
Shell=True, check=True,
executable='/bin/bash')
subprocess
は親とは別であり、変更できませんやや一般的な間違いは、次のようなことをすることです
subprocess.run('foo=bar', Shell=True)
subprocess.run('echo "$foo"', Shell=True) # Doesn't work
エレガンスの欠如はさておき、「サブプロセス」という名前の「サブ」部分の基本的な理解不足を裏付けています。
子プロセスはPythonとは完全に独立して実行され、終了時には、Pythonは何をしたかわかりません(終了ステータスと子プロセスからの出力から推測できるあいまいなインジケーターは別として)。通常、子供は親の環境を変更できません。変数を設定したり、作業ディレクトリを変更したり、多くの言葉で言えば、親の協力なしに親と通信することはできません。
この特定の場合の即時修正は、単一のサブプロセスで両方のコマンドを実行することです。
subprocess.run('foo=bar; echo "$foo"', Shell=True)
ただし、この特定のユースケースではシェルはまったく必要ありません。覚えておいて、あなたは現在のプロセスの環境を操作することができます(したがって、その子も)
os.environ['foo'] = 'bar'
または、環境設定を子プロセスに渡します
subprocess.run('echo "$foo"', Shell=True, env={'foo': 'bar'})
(明らかなリファクタリングsubprocess.run(['echo', 'bar'])
は言うまでもありませんが、echo
は、もちろん、最初にサブプロセスで実行する何かの悪い例です)。
これは少し疑わしいアドバイスです。 PythonインタープリターをPythonスクリプトからサブプロセスとして実行することが理にかなっている場合や、絶対要件である場合もあります。しかし、非常に頻繁に、正しいアプローチは、呼び出し元のスクリプトに他のPythonモジュールをimport
して、その関数を直接呼び出すことです。
他のPythonスクリプトが制御下にあり、モジュールではない場合は、 を1つの に変換することを検討してください。 (この答えはすでに長すぎるので、ここでは詳細を掘り下げません。)
並列処理が必要な場合は、 multiprocessing
モジュールを使用して、サブプロセスでPython関数を実行できます。 単一のプロセスで複数のタスクを実行する threading
もあります(より軽量で、より制御しやすくなりますが、プロセス内のスレッドが密結合され、単一の にバインドされるという制約もあります。 _ギル_ 。)
サブプロセスで呼び出します
import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")
サーバーにスワップモジュールがないために発生しているエラーのようです。サーバーにスワップをインストールしてからスクリプトを再度実行する必要があります。
コマンドを実行するために、パラメータ-cを付けてbashプログラムを使用することができます。
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])
あなたは 'サブプロセス'を使うことができますが、私はそれが 'Pythonic'のやり方ではないといつも感じました。そこで私はSultan(恥知らずなプラグ)を作成しました。それはコマンドライン関数を実行するのを簡単にします。
エラーによると、サーバー上にswapという名前のパッケージがありません。この/usr/bin/cwm
はそれを必要とします。 Ubuntu/Debianを使っている場合は、aptitudeを使ってpython-swap
をインストールしてください。
シェルなしでコマンドを実行するには、コマンドをリストとして渡し、 [subprocess]
を使用してPythonでリダイレクトを実装します。
#!/usr/bin/env python
import subprocess
with open('test.nt', 'wb', 0) as file:
subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
stdout=file)
注:末尾に> test.nt
はありません。 stdout=file
はリダイレクトを実装します。
Pythonでシェルを使用してコマンドを実行するには、コマンドを文字列として渡し、Shell=True
を有効にします。
#!/usr/bin/env python
import subprocess
subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
Shell=True)
シェルが出力のリダイレクトを担当します(> test.nt
がコマンドにあります)。
Bashismを使用するbashコマンドを実行するには、bash実行可能ファイルを明示的に指定します。例えば、 bashプロセス置換をエミュレートする のようにします。
#!/usr/bin/env python
import subprocess
subprocess.check_call('program <(command) <(another-command)',
Shell=True, executable='/bin/bash')
'os.popen'も使えます。例:
import os
command = os.popen('ls -al')
print(command.read())
print(command.close())
出力:
total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root 77 ago 13 21:53 test.py
None
これを行うPythonicの方法は、 subprocess.Popen
を使用することです。
subprocess.Popen
は、最初の要素が実行されるコマンドで、その後にコマンドライン引数が続くリストを取ります。
例として:
import subprocess
args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line
args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line