Frage

Wie führe ich die folgenden Shell-Befehl mit der Python subprocess Modul?

echo "input data" | awk -f script.awk | sort > outfile.txt

Die Eingangsdaten werden aus einem String kommen, so dass ich nicht wirklich echo brauchen. Ich habe so weit gekommen, kann jemand erklären, wie ich es Rohr durch sort bekommen auch?

p_awk = subprocess.Popen(["awk","-f","script.awk"],
                          stdin=subprocess.PIPE,
                          stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )

UPDATE : Beachten Sie, dass die akzeptierte Antwort unten eigentlich nicht die Frage beantworten, wie gefragt, wie ich glaube S.Lott richtig ist, und es ist besser, um zu vermeiden, dieses Problem in erster Linie zu lösen !

War es hilfreich?

Lösung

Sie würden ein wenig glücklicher mit der folgenden sein.

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" )

Delegierter Teil der Arbeit an der Schale. Lassen Sie es zwei Prozesse mit einer Pipeline verbinden.

Sie würden viel glücklicher sein Umschreiben 'script.awk' in Python, die Beseitigung awk und die Pipeline.

Bearbeiten . Einige der Gründe für diese awk was darauf hindeutet, ist nicht zu helfen.

[Es gibt zu viele Gründe, über Kommentare zu reagieren.]

  1. Awk fügt einen Schritt keinen signifikanten Wert. Es gibt nichts, das Besondere an awk die Verarbeitung, die Python nicht behandelt.

  2. Das Pipelining von awk zu sortieren, für große Datenmengen können verstrichene Bearbeitungszeit verbessern. Für kurze Sätze von Daten, hat es keinen signifikanten Nutzen. Eine schnelle Messung von awk >file ; sort file und awk | sort wird zeigen, die Parallelität hilft. Mit Art, hilft es selten, weil Art nicht ein einmal durch den Filter ist.

  3. Die Einfachheit des „Pythons zu sortieren“ Verarbeitung (anstelle von „Python awk zu sortieren“) verhindert, dass die genaue Art von Fragen hier gefragt zu werden.

  4. Python - während wortreicher als awk -. Ist auch ausdrücklich in den awk gewisse implizite Regeln hat, die Neulinge undurchsichtig sind, und verwirrend für Nicht-Spezialisten

  5. Awk (wie das Shell-Skript selbst) fügt noch eine weitere Programmiersprache. Wenn all dies kann in einer Sprache (Python) durchgeführt werden, wodurch die Schale und die awk-Programmierung eliminiert zwei Programmiersprachen, so dass jemand auf den Wert erzeugenden Teile der Aufgabe konzentrieren.

Fazit: awk kann nicht signifikanten Mehrwert. In diesem Fall awk ist ein Nettokosten; hinzugefügt genug Komplexität, dass es notwendig war, diese Frage zu stellen. Entfernen von awk wird ein Nettogewinn sein.

Seitenleiste Warum Bau einer Pipeline (a | b) ist so hart.

Wenn die Schale mit a | b konfrontiert hat folgendes zu tun.

  1. ein Kind Prozess der ursprünglichen Shell-Gabel. Dies wird schließlich werden b.

  2. Erstellen Sie ein o Rohr. (Kein Python subprocess.PIPE) aber rufen os.pipe(), die zwei neue Datei-Deskriptoren zurückgibt, die über den gemeinsamen Puffer verbunden sind. An diesem Punkt hat der Prozess stdin, stdout, stderr von seinem übergeordneten plus eine Datei, die „eine der stdout“ und „b stdin“ wird.

  3. ein Kind Gabel. Das Kind ersetzt seine stdout mit dem stdout der neuen ein. Exec den a Prozess.

  4. Das b Kind schließt seine stdin mit dem neuen B stdin ersetzt. Exec den b Prozess.

  5. Das b Kind wartet ein abzuschließen.

  6. Die Eltern warten b abzuschließen.

Ich denke, dass die oben rekursiv a | b | c zum Laichen verwendet werden können, aber Sie müssen implizit lange Pipelines klammern, sie zu behandeln, als ob sie a | (b | c) sind.

Da Python hat os.pipe(), os.exec() und os.fork(), und Sie können sys.stdin und sys.stdout ersetzen, gibt es eine Möglichkeit, die oben in reinem Python zu tun. Tatsächlich können Sie in der Lage sein, mit os.pipe() und subprocess.Popen einige Verknüpfungen zu trainieren.

Allerdings ist es einfacher, dass der Betrieb an die Schale zu übertragen.

Andere Tipps

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)

eine Shell-Pipeline zu emulieren:

from subprocess import check_call

check_call('echo "input data" | a | b > outfile.txt', shell=True)

ohne die Shell aufrufen (siehe 17.1.4.2. Ersetzen Schale Pipeline ):

#!/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 bietet einige Syntax Zucker:

#!/usr/bin/env python
from plumbum.cmd import a, b # magic

(a << "input data" | b > "outfile.txt")()

Die Analog von:

#!/bin/sh
echo "input data" | awk -f script.awk | sort > outfile.txt

ist:

#!/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 diese ziemlich gut abgedeckt. Gibt es einen Teil davon Sie nicht verstanden?

Ihr Programm wäre ziemlich ähnlich sein, aber die zweite Popen hätte stdout = in eine Datei, und Sie würden den Ausgang seines .communicate() nicht benötigen.

Inspiriert von @ Cristian Antwort. Ich traf das gleiche Problem, aber mit einem anderen Befehl. Also ich meine getestet Beispiel setzen, die meiner Meinung nach könnte hilfreich sein:

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()

Dies wird getestet.

Was getan wurde,

  • erklärt faul grep Ausführung mit stdin von Rohr. Dieser Befehl wird am ps Befehlsausführung ausgeführt werden, wenn das Rohr mit dem stdout von ps gefüllt wird.
  • Genannt der Primärbefehl ps mit stdout an dem Rohr durch den grep Befehl gerichtet ist.
  • Grep mitgeteilt aus dem Rohr stdout zu erhalten.

Ich mag diese Art und Weise, weil es mit subprocess Schnittstellen sanft umhüllt Konzeption natürliche Rohr ist.

Die akzeptierte Antwort ausweicht das Problem. Hier ist ein Ausschnitt der Ausgabe von mehreren Prozessen, die Ketten: Beachten Sie, dass es auch den druckt (etwas) äquivalenten Shell-Befehl, so dass Sie es ausführen können und sicherstellen, dass die Ausgabe korrekt ist.

#!/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)

EDIT: pipes unter Windows verfügbar ist, aber entscheidend ist, scheint nicht wirklich Arbeit auf Windows. Siehe Anmerkungen unten.

Die Python-Standardbibliothek enthält jetzt das pipes Modul dies für den Umgang mit:

https://docs.python.org/2/library/pipes.html , https://docs.python.org/3.4/library/pipes.html

Ich bin mir nicht sicher, wie lange dieses Modul gewesen ist, aber dieser Ansatz scheint wesentlich einfacher als Ausmisten etwa mit subprocess.

Die bisherigen Antworten verpasst einen wichtigen Punkt. Shell Pipeline Ersetzen ist grundsätzlich korrekt, wie darauf hingewiesen von geocar. Es ist fast ausreichend communicate auf dem letzten Element des Rohres zu laufen.

Das verbleibende Problem ist vorbei, die Eingabedaten an der Pipeline. Mit mehreren Teilprozessen, hat ein einfaches communicate(input_data) auf das letzte Element nicht funktionieren - es hängt immer. Sie benötigen einen eine Pipeline und ein Kind manuell wie folgt erstellen:

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())

Jetzt das Kind stellt die Eingabe durch das Rohr, und die Elterngespräche kommunizieren (), die wie erwartet funktioniert. Mit diesem Ansatz können Sie beliebig lange Rohrleitungen erstellen, ohne auf „Delegieren Teil der Arbeit an die Schale“. Leider ist die subprocess Dokumentation dies nicht erwähnt.

Es gibt Möglichkeiten, die gleiche Wirkung ohne Rohre zu erreichen:

from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)

Jetzt stdin=tf für p_awk verwenden. Es ist eine Frage des Geschmacks, was Sie bevorzugen.

Die oben ist noch nicht 100% entspricht Pipelines einzuschlagen, weil die Signalverarbeitung unterscheidet. Sie können das sehen, wenn Sie ein anderes Rohrelement hinzufügen, das die Ausgabe von sort abschneidet, z.B. head -n 10. Mit dem obigen Code druckt sort a „Broken pipe“ Fehlermeldung an stderr. Diese Meldung wird nicht angezeigt, wenn Sie die gleiche Pipeline in der Shell ausgeführt. (Das ist der einzige Unterschied ist aber, das Ergebnis in stdout gleich). Der Grund scheint zu sein, dass die Python Popen Sätze für SIG_IGN SIGPIPE, während die Schale es bei SIG_DFL verlässt und Signalverarbeitung des sort unterscheidet sich in diesen beiden Fällen.

Für mich ist der unter Ansatz ist die sauberste und am einfachsten zu lesen

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()

, die wie so genannt werden kann:

string_to_2_procs_to_file('input data', ['awk', '-f', 'script.awk'], ['sort'], 'output.txt')
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top