注文する
$ 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")
する必要があります。
答え2
コマンド文字列には、split()
などのシェルで処理する必要がある文字が含まれているため、使用できません。使用バージョン:~
|
process = subprocess.Popen(my_command, stdout=subprocess.PIPE, shell=True)