Wie leite ich die Ausgabe mit einem Unterprozess in Python um?

96

Was ich in der Kommandozeile mache:

cat file1 file2 file3 > myfile

Was ich mit Python machen möchte:

import subprocess, shlex
my_cmd = 'cat file1 file2 file3 > myfile'
args = shlex.split(my_cmd)
subprocess.call(args) # spits the output in the window i call my python program
Katatemypythoncode
quelle
Wenn Sie einen solchen Befehl im Unterprozess ausführen, erhalten Sie keine Ausgabe. Vielleicht möchten Sie es ausführen, ohne dass > myfile die Ausgabe von cat file1 file2 file3 in Python umleitet ?
PoltoS
@PoltoS Ich möchte einige Dateien verbinden und dann die resultierende Datei verarbeiten. Ich dachte, die Verwendung von Katze sei die einfachste Alternative. Gibt es einen besseren / pythonischen Weg, dies zu tun?
Katatemypythoncode
os.sendfile()-basierte Lösung ist möglich, siehe Reproduzieren des Unix-Cat-Befehls in Python
JFS
1
Ich denke, dass die Ausgabeumleitung ('>' oder '>>') im Unterprozess nicht funktioniert. Open (zumindest in Python 2.7) (im Shell = True-Modus) In diesem Beispiel können Sie, wie andere betonen, umgehen Dies, indem keine Umleitung verwendet wird, aber in anderen Fällen ist die Umleitung nützlich. Wenn Umleitung oder Rohrleitungen im Unterprozess nicht unterstützt werden. Öffnen sollte dokumentiert werden (und / oder os.system () sollte nicht veraltet sein, bis dies behoben ist)
Ribo

Antworten:

19

UPDATE: Von os.system wird abgeraten, obwohl es in Python 3 noch verfügbar ist.


Verwendung os.system:

os.system(my_cmd)

Wenn Sie wirklich einen Unterprozess verwenden möchten, finden Sie hier die Lösung (meistens aus der Dokumentation für den Unterprozess entnommen):

p = subprocess.Popen(my_cmd, shell=True)
os.waitpid(p.pid, 0)

OTOH, Sie können Systemaufrufe vollständig vermeiden:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)
Marcelo Cantos
quelle
1
Es funktioniert, aber ich möchte Sie dann fragen: Was bringt die Unterprozessbibliothek, wenn os.system den Job bereits erledigt? Ich habe das Gefühl, ich hätte stattdessen einen Unterprozess verwenden sollen, da es sich um eine Bibliothek handelt, die dieser Aufgabe gewidmet ist. Da ich dies jedoch nur für mich selbst mache, kann ich os.system diesmal problemlos verwenden.
Katatemypythoncode
Die Unterprozessbibliothek ist viel flexibler als os.systemund kann os.systempräzise modelliert werden , ist aber auch komplexer zu bearbeiten.
Marcelo Cantos
13
os.systemkam vorher subprocess. Ersteres ist eine Legacy-API, die Letzteres ersetzen möchte.
Santa
5
@catatemypythoncode: Sie sollten nicht os.system()oder verwenden shell=True. Verwenden Sie den stdoutParameter wie in der Antwort von Ryan Thompson gezeigt, um die Ausgabe eines Unterprozesses umzuleiten . Obwohl Sie catin Ihrem Fall keinen Unterprozess ( ) benötigen , können Sie Dateien mit reinem Python verketten.
JFS
4
OTOH = Auf der anderen Seite
Cephlin
271

In Python 3.5 und höher die Ausgabe zu umleiten, geben Sie einfach eine offene Datei - Handle für das stdoutArgument subprocess.run:

# Use a list of args instead of a string
input_files = ['file1', 'file2', 'file3']
my_cmd = ['cat'] + input_files
with open('myfile', "w") as outfile:
    subprocess.run(my_cmd, stdout=outfile)

Wie andere betont haben, ist die Verwendung eines externen Befehls wie catfür diesen Zweck völlig irrelevant.

Ryan C. Thompson
quelle
9
Dies sollte die Antwort auf die allgemeine Frage der Rohrleitungen sein, wenn die Shell von Python
Kaushik Ghose
46
Dies ist die richtige Antwort, nicht die als richtig gekennzeichnete.
Justin Blake
7
Für Python 3.5+ verwenden, subprocess.run(my_cmd, stdout=outfile)das ersetztsubprocess.call(...)
Austin Yates
1
Bemerkenswerterweise funktioniert dies nicht mit benutzerdefinierten Dateiobjekten, wenn sie kein Filenofeld haben (wenn sie keine echte Datei sind)
Eliezer Miron
1
Da Python <3.5 ab sofort veraltet ist, habe ich die Antwort mit Ihrem Kommentar @AustinYates aktualisiert.
Greg Dubicki
5

@PoltoS Ich möchte einige Dateien verbinden und dann die resultierende Datei verarbeiten. Ich dachte, die Verwendung von Katze sei die einfachste Alternative. Gibt es einen besseren / pythonischen Weg, dies zu tun?

Natürlich:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())
SingleNegationElimination
quelle
1
size = 'ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 dump.mp4 > file'
proc = subprocess.Popen(shlex.split(size), shell=True)
time.sleep(1)
proc.terminate() #proc.kill() modify it by a suggestion
size = ""
with open('file', 'r') as infile:
    for line in infile.readlines():
        size += line.strip()

print(size)
os.remove('file')

Wenn Sie subprocess , muss der Prozess killed.This sein ist ein example.If Sie den Prozess nicht töten, Datei leer sein wird , und Sie können nothing.It lesen Sie weiter laufen kann Windows - .Ich kann leider nicht sicher, dass es laufen unter Unix.

Wyx
quelle
1
Es ist ein schlechtes Code - Beispiel (es wird nicht auf Unix arbeiten, denn es zeigt , schlechte Praktiken for line in .readlines():, s +=) und proc.kill()im Allgemeinen zu Informationsverlust führen kann (es nicht zulässt , dass die subprocess sanft beenden (unter Unix) - unflushed Inhalt verloren ). Auf jeden Fall ist der Hinweis zum Puffern als Kommentar besser geeignet.
JFS
Ich starte es unter Windows ist OK (weil kill gleichbedeutend ist mit dem Beenden unter Windows). Unter Unix sollten Sie möglicherweise proc.terminate () verwenden. @ JF Sebastian Ich habe kein Unix-System auf meinem Computer.
Wyx
Wenn Sie unter Windows arbeiten shlex.split(), löschen shell=True, löschen >file, löschen open(), löschen usw. und verwenden Sie stdout=PIPE: Timer(1, proc.terminate).start(); output = proc.communicate()[0]stattdessen. Hier ist ein vollständiges Beispiel . Weitere Lösungen: Stoppen Sie das Lesen der Prozessausgabe in Python, ohne zu hängen? Hinweis: Die Frage, dass Sie den untergeordneten Prozess manuell beenden müssen, ist nicht erforderlich. Sie können auch andere Probleme beheben, z. B. kann sich ein Prozess anders verhalten, wenn seine Standardausgabe eine tty ist, aber nicht zum Thema gehört.
JFS
0

Ein interessanter Fall wäre das Aktualisieren einer Datei durch Anhängen einer ähnlichen Datei. Dann müsste man dabei keine neue Datei erstellen. Dies ist besonders nützlich, wenn eine große Datei angehängt werden muss. Hier ist eine Möglichkeit, die temporäre Befehlszeile direkt von Python aus zu verwenden.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
DJJ
quelle