web-dev-qa-db-ja.com

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

コマンド

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

GNU Parallel を使用して、.txtの下のすべての~/foo/ファイルを印刷します。

pythonスクリプトがあり、このbashコマンドを呼び出します:

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]

スクリプトの何が問題になっていますか?

6

実際には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)
    _

    これはより堅牢で、余分なシェルを生成しませんが、一方ではよりタイピングします。これを行うと、noシェル置換が発生することに注意してください。あなたの場合、それはあなたがそれを必要としているようには見えませんが、もしあなたが例えば_~_を使いたいなら、あなたはそれをPython(eg os.getenv("HOME"))。

10
marinus

コマンド文字列には、_~_や_|_などのシェルで処理する必要のある文字が含まれているため、split()することはできません。バージョンを使用:

_process = subprocess.Popen(my_command, stdout=subprocess.PIPE, Shell=True)
_
0
meuh