Pythonから呼び出すときにこのbashコマンドが実行されないのはなぜですか?

Pythonから呼び出すときにこのbashコマンドが実行されないのはなぜですか?

注文する

$ find ~/foo/ -type f -iname "*.txt" -print0 | parallel -0 cat

使用GNUパラレル.txt以下のすべてのファイルを印刷してください~/foo/

このbashコマンドを呼び出すPythonスクリプトがあります。

import subprocess, sys

def runBashCommand(my_command):
    process = subprocess.Popen(my_command.split(), stdout=subprocess.PIPE)
    output  = process.communicate()[0]
    return None

def makeCommand(my_path):
    return "find {} -type f -iname \"*.txt\" -print0 | parallel -0 cat".format(my_path)

発行された

>>> makeCommand('~/foo/')

返品

'find ~/foo/ -type f -iname "*.txt" -print0 | parallel -0 cat'

しかし発行

>>> runBashCommand(makeCommand('~/foo/'))

エラー発生

find: paths must precede expression: |
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

私のスクリプトに何の問題がありますか?

ベストアンサー1

bash実際にはコマンドを実行しません。あなたがやっていることは、実行可能ファイルを直接実行して引数を渡すことです。

次のスクリプトを試して、何が起こっているのかを確認してください。

import subprocess
p = subprocess.Popen(["echo", "a", "b", "|", "rev"], stdout=subprocess.PIPE)
print p.communicate()

出力は次のとおりです。

('a b | rev\n', None)

リダイレクトは発生せず、「|」はそのまま渡されます。つまり、まるで自分が入力したのと同じですfind ... \| parallel ...。したがって、エラーが発生しました。

問題を解決する方法は2つあります。

  • 最も簡単な方法shell=Trueはに渡すことですsubprocess.Popen。これにより、必要なすべての項目を含むシェルを介して実行されます。これを行うには、配列の代わりに文字列も渡す必要があります。

    import subprocess
    p = subprocess.Popen("echo a b | rev", stdout=subprocess.PIPE, shell=True)
    print p.communicate()
    
    # Result: ('b a\n', None)
    

    これにより、文字列のパラメータ置換には非常に注意してください。

  • 強力なアプローチ:Pythonを使用して2つのプロセスを開き、一緒に接続します。

    import subprocess
    # First command
    p1 = subprocess.Popen(["echo", "a", "b"], stdout=subprocess.PIPE)
    # Second command's input linked to the first one's output
    p2 = subprocess.Popen(["rev"], stdin=p1.stdout, stdout=subprocess.PIPE)
    # Read from p2 to get the output
    print p2.communicate()
    
    # Result: ('b a\n', None)
    

    これはより強力で追加のシェルを生成しませんが、他方でより多くのタイピングが必要です。これにより、気づくいいえシェル交換の発生。あなたの場合は必要ないようですが、使用するには~Python(たとえば)を介してインポートos.getenv("HOME")する必要があります。

おすすめ記事