web-dev-qa-db-ja.com

PythonでBashコマンドを実行する

私のローカルマシンで、私はこの行を含む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)が原因で)、そのエラーの前には端末にコマンドを表示します。それから私はちょうどその出力をコピーして端末にコピーペーストをしてエンターを押すとそれは動作します...

何が起こっているのかについての手がかりがありますか?

214
mkn

os.systemを使用しないでください。それは サブプロセス を支持して非推奨になりました。 のドキュメントから : "このモジュールはいくつかの古いモジュールや関数を置き換えようとしています:os.systemos.spawn"。

あなたの場合のように:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
252
user225312

ここで以前の回答を多少拡張するために、一般的に見落とされている多くの詳細があります。

  • subprocess.run()よりもsubprocess.check_call()を、subprocess.call()よりもsubprocess.Popen()よりもos.system()よりもos.popen()を優先します
  • text=True、別名universal_newlines=Trueを理解し、おそらく使用してください。
  • Shell=TrueまたはShell=Falseの意味と、それがクォートを変更する方法とシェルの便利さを理解します。
  • shとBashの違いを理解する
  • サブプロセスがその親からどのように分離され、通常は親を変更できないかを理解します。
  • PythonインタープリターをPythonのサブプロセスとして実行しないでください。

これらのトピックについては、以下でさらに詳しく説明します。

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と同等です

高レベルAPIと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=Truesubprocess.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=TrueShell=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コード内からの実行をより詳細に制御できます。
  • そうは言っても、複雑なシェルパイプラインは退屈で、時にはPythonでの再実装が困難です。

また、リファクタリングされたコードは、非常に簡潔な構文を使用して、シェルが実際にどれだけ機能するかを示しています-良くも悪くも。 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標準ライブラリ のモジュール、またはより現代的で汎用性の高い多数のサードパーティの競合製品)。
  • ジョブ制御を使用すると、ジョブを中断したり、バックグラウンドで実行したり、フォアグラウンドに戻したりすることができます。プロセスを停止および続行するための基本的なUnixシグナルはもちろんPythonからも利用できます。しかし、ジョブはシェルでの高レベルの抽象化であり、プロセスグループなどが含まれます。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インタープリターをPythonスクリプトからサブプロセスとして実行することが理にかなっている場合や、絶対要件である場合もあります。しかし、非常に頻繁に、正しいアプローチは、呼び出し元のスクリプトに他のPythonモジュールをimportして、その関数を直接呼び出すことです。

他のPythonスクリプトが制御下にあり、モジュールではない場合は、 を1つの に変換することを検討してください。 (この答えはすでに長すぎるので、ここでは詳細を掘り下げません。)

並列処理が必要な場合は、 multiprocessingモジュールを使用して、サブプロセスでPython関数を実行できます。 単一のプロセスで複数のタスクを実行する threading もあります(より軽量で、より制御しやすくなりますが、プロセス内のスレッドが密結合され、単一の にバインドされるという制約もあります。 _ギル_ 。)

87
tripleee

サブプロセスで呼び出します

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

サーバーにスワップモジュールがないために発生しているエラーのようです。サーバーにスワップをインストールしてからスクリプトを再度実行する必要があります。

38
Jakob Bowyer

コマンドを実行するために、パラメータ-cを付けてbashプログラムを使用することができます。

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])
18
Razor

あなたは 'サブプロセス'を使うことができますが、私はそれが 'Pythonic'のやり方ではないといつも感じました。そこで私はSultan(恥知らずなプラグ)を作成しました。それはコマンドライン関数を実行するのを簡単にします。

https://github.com/aeroxis/sultan

9
David Daniel

エラーによると、サーバー上にswapという名前のパッケージがありません。この/usr/bin/cwmはそれを必要とします。 Ubuntu/Debianを使っている場合は、aptitudeを使ってpython-swapをインストールしてください。

8
kichik

シェルなしでコマンドを実行するには、コマンドをリストとして渡し、 [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')
2
jfs

'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
1
ricardo130

これを行う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
0
RewordedAnswers