Python subprocess
モジュールを使用して次のシェルコマンドを実行するにはどうすればよいですか?
echo "input data" | awk -f script.awk | sort > outfile.txt
入力データは文字列から取得されるため、実際にはecho
は必要ありません。私はこれまでのところ、私もsort
を介してパイプする方法を説明できますか?
p_awk = subprocess.Popen(["awk","-f","script.awk"],
stdin=subprocess.PIPE,
stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )
[〜#〜] update [〜#〜]:以下の受け入れられた回答は実際に質問に答えていないが、S .Lottは正しいので、そもそもその問題を解決する必要はありません。
あなたは次のことで少し幸せになるでしょう。
import subprocess
awk_sort = subprocess.Popen( "awk -f script.awk | sort > outfile.txt",
stdin=subprocess.PIPE, Shell=True )
awk_sort.communicate( b"input data\n" )
作業の一部をシェルに委任します。 2つのプロセスをパイプラインで接続します。
'script.awk'をPythonに書き直して、awkとパイプラインを削除する方がずっと幸せです。
編集。 awkが役に立たないことを示唆する理由のいくつか。
[コメントで回答する理由は多すぎます。]
Awkは重要な価値のないステップを追加しています。 Pythonが処理しないawkの処理についてユニークなものはありません。
Awkからソートへのパイプライン処理により、大量のデータが処理されるため、経過処理時間が改善される場合があります。短いデータセットの場合、大きな利点はありません。 awk >file ; sort file
およびawk | sort
をすばやく測定すると、同時実行の助けがわかります。並べ替えでは、並べ替えは1回限りのフィルターではないため、ほとんど役立ちません。
「Python to sort」処理の単純さ(「Python to awk to sort」の代わり)は、ここで尋ねられる質問の正確な種類を防ぎます。
Pythonはawkよりも冗長ですが、awkが初心者には不透明で、専門家でない人には混乱を招くような暗黙のルールがある場合にも明示的です。
(シェルスクリプト自体と同様に)Awkは、さらに別のプログラミング言語を追加します。これらすべてを1つの言語(Python)で実行できる場合、シェルとawkプログラミングを排除すると2つのプログラミング言語が排除され、誰かがタスクの価値を生み出す部分に集中できるようになります。
結論:awkは大きな価値を追加できません。この場合、awkは正味コストです。この質問をするのに必要なほど複雑でした。 awkを削除すると、純益になります。
サイドバーパイプライン(a | b
)の構築が非常に難しい理由。
シェルがa | b
に直面したとき、次のことをしなければなりません。
元のシェルの子プロセスをフォークします。これは最終的にbになります。
OSパイプを構築します。 (Python subprocess.PIPE)ではなく、共通バッファーを介して接続された2つの新しいファイル記述子を返すos.pipe()
を呼び出します。この時点で、プロセスにはstdin、stdout、stderrがあります)その親と、「a's stdout」および「b's stdin」になるファイル。
子供をフォークします。子は、その標準出力を新しいaの標準出力に置き換えます。 a
プロセスを実行します。
Bの子がクローズすると、そのstdinが新しいbのstdinに置き換えられます。 b
プロセスを実行します。
Bの子は、aが完了するのを待ちます。
親はbが完了するのを待っています。
上記を再帰的に使用してa | b | c
を生成できると思いますが、a | (b | c)
のように長いパイプラインを暗黙的に括弧で囲む必要があります。
Pythonにはos.pipe()
、os.exec()
、os.fork()
があり、sys.stdin
とsys.stdout
を置き換えることができるので、純粋なPythonで上記を行う方法実際、os.pipe()
とsubprocess.Popen
を使用して、いくつかのショートカットを作成できる場合があります。
ただし、その操作をシェルに委任する方が簡単です。
import subprocess
some_string = b'input_data'
sort_out = open('outfile.txt', 'wb', 0)
sort_in = subprocess.Popen('sort', stdin=subprocess.PIPE, stdout=sort_out).stdin
subprocess.Popen(['awk', '-f', 'script.awk'], stdout=sort_in,
stdin=subprocess.PIPE).communicate(some_string)
シェルパイプラインをエミュレートするには:
from subprocess import check_call
check_call('echo "input data" | a | b > outfile.txt', Shell=True)
シェルを呼び出さずに( 17.1.4.2。シェルパイプラインの置き換え を参照):
#!/usr/bin/env python
from subprocess import Popen, PIPE
a = Popen(["a"], stdin=PIPE, stdout=PIPE)
with a.stdin:
with a.stdout, open("outfile.txt", "wb") as outfile:
b = Popen(["b"], stdin=a.stdout, stdout=outfile)
a.stdin.write(b"input data")
statuses = [a.wait(), b.wait()] # both a.stdin/stdout are closed already
plumbum
は、いくつかの構文シュガーを提供します。
#!/usr/bin/env python
from plumbum.cmd import a, b # magic
(a << "input data" | b > "outfile.txt")()
アナログ:
#!/bin/sh
echo "input data" | awk -f script.awk | sort > outfile.txt
は:
#!/usr/bin/env python
from plumbum.cmd import awk, sort
(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
http://www.python.org/doc/2.5.2/lib/node535.html はこれをかなりよくカバーしていました。あなたが理解していなかったこの部分がありますか?
プログラムはかなり似ていますが、2番目のPopen
にはファイルへのstdout =があり、その.communicate()
の出力は必要ありません。
受け入れられた答えは、問題を回避することです。複数のプロセスの出力を連結するスニペットを次に示します。また、実行することで出力が正しいことを確認できるように、(同等の)同等のシェルコマンドも出力することに注意してください。
#!/usr/bin/env python3
from subprocess import Popen, PIPE
# cmd1 : dd if=/dev/zero bs=1m count=100
# cmd2 : gzip
# cmd3 : wc -c
cmd1 = ['dd', 'if=/dev/zero', 'bs=1M', 'count=100']
cmd2 = ['tee']
cmd3 = ['wc', '-c']
print(f"Shell style : {' '.join(cmd1)} | {' '.join(cmd2)} | {' '.join(cmd3)}")
p1 = Popen(cmd1, stdout=PIPE, stderr=PIPE) # stderr=PIPE optional, dd is chatty
p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd3, stdin=p2.stdout, stdout=PIPE)
print("Output from last process : " + (p3.communicate()[0]).decode())
# thoretically p1 and p2 may still be running, this ensures we are collecting their return codes
p1.wait()
p2.wait()
print("p1 return: ", p1.returncode)
print("p2 return: ", p2.returncode)
print("p3 return: ", p3.returncode)
@Cristianの回答に触発されました。同じ問題に出会ったが、コマンドは異なる。そこで、テスト済みの例を紹介します。これは役立つと思われます。
grep_proc = subprocess.Popen(["grep", "rabbitmq"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
subprocess.Popen(["ps", "aux"], stdout=grep_proc.stdin)
out, err = grep_proc.communicate()
これはテスト済みです。
grep
実行を宣言しました。このコマンドは、パイプがps
のstdoutで満たされるときに、ps
コマンドの実行時に実行されます。ps
コマンドで使用されるパイプに向けられたstdoutを使用して、プライマリコマンドgrep
を呼び出しました。subprocess
インターフェイスで優しくラップされた自然なパイプの概念なので、この方法が好きです。
前の答えは重要な点を逃しました。 シェルパイプラインの置き換え は、geocarが指摘したように、基本的に正しいです。パイプの最後の要素でcommunicate
を実行するには、almostで十分です。
残りの問題は、入力データをパイプラインに渡すことです。複数のサブプロセスでは、最後の要素の単純なcommunicate(input_data)
は機能しません-永久にハングします。次のように、手動でパイプラインと子を作成する必要があります。
import os
import subprocess
input = """\
input data
more input
""" * 10
rd, wr = os.pipe()
if os.fork() != 0: # parent
os.close(wr)
else: # child
os.close(rd)
os.write(wr, input)
os.close(wr)
exit()
p_awk = subprocess.Popen(["awk", "{ print $2; }"],
stdin=rd,
stdout=subprocess.PIPE)
p_sort = subprocess.Popen(["sort"],
stdin=p_awk.stdout,
stdout=subprocess.PIPE)
p_awk.stdout.close()
out, err = p_sort.communicate()
print (out.rstrip())
これで、子はパイプを介して入力を提供し、親は予想どおりに動作するCommunicate()を呼び出します。このアプローチを使用すると、「作業の一部をシェルに委任する」ことなく、任意の長いパイプラインを作成できます。残念ながら subprocess documentation ではこれについて言及していません。
パイプなしで同じ効果を達成する方法があります:
from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)
stdin=tf
にp_awk
を使用します。それは好みの好みの問題です。
上記は、信号処理が異なるため、bashパイプラインと100%同等ではありません。 sort
の出力を切り捨てる別のパイプ要素を追加すると、これを確認できます。 head -n 10
。上記のコードでは、sort
は「壊れたパイプ」エラーメッセージをstderr
に出力します。シェルで同じパイプラインを実行すると、このメッセージは表示されません。 (それが唯一の違いですが、stdout
の結果は同じです)。理由は、PythonのPopen
がSIGPIPE
に対してSIG_IGN
を設定しているのに対し、シェルはSIG_DFL
でそれを残し、sort
のシグナル処理が異なるためと思われますこれら2つの場合。
EDIT:pipes
はWindowsで使用できますが、実際にはworkWindowsの場合。以下のコメントを参照してください。
Python標準ライブラリには、これを処理するためのpipes
モジュールが含まれています。
https://docs.python.org/2/library/pipes.html 、 https://docs.python.org/3.4/library/pipes.html
このモジュールがどのくらいの期間使用されているかはわかりませんが、このアプローチはsubprocess
をいじくり回すよりもはるかに簡単なようです。
私にとって、以下のアプローチは最もきれいで読みやすいです
from subprocess import Popen, PIPE
def string_to_2_procs_to_file(input_s, first_cmd, second_cmd, output_filename):
with open(output_filename, 'wb') as out_f:
p2 = Popen(second_cmd, stdin=PIPE, stdout=out_f)
p1 = Popen(first_cmd, stdout=p2.stdin, stdin=PIPE)
p1.communicate(input=bytes(input_s))
p1.wait()
p2.stdin.close()
p2.wait()
次のように呼び出すことができます:
string_to_2_procs_to_file('input data', ['awk', '-f', 'script.awk'], ['sort'], 'output.txt')