Come reindirizzare l'output con sottoprocesso in Python?


97

Cosa faccio nella riga di comando:

cat file1 file2 file3 > myfile

Cosa voglio fare con Python:

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

L'esecuzione di un tale comando in sottoprocesso non fornirà alcun output. Potresti volerlo eseguire senza > miofile reindirizzare l'output da cat file1 file2 file3 in python?
PoltoS

@PoltoS Voglio unire alcuni file e quindi elaborare il file risultante. Pensavo che usare il gatto fosse l'alternativa più semplice. C'è un modo migliore / pitonico per farlo?
catatemypythoncode

os.sendfile()-è possibile una soluzione basata su, vedere Riprodurre il comando cat unix in python
jfs

1
Penso che il reindirizzamento dell'output ('>' o '>>') non funzioni in subprocess.Popen (almeno in Python 2.7) (in shell = True mode) In questo esempio, come altri sottolineano, puoi aggirare questo non utilizzando il reindirizzamento, ma in altri casi il reindirizzamento è utile. Se il reindirizzamento o il piping non sono supportati in subprocess.Popen deve essere documentato (e / o os.system () non dovrebbe essere deprecato fino a quando non viene risolto)
Ribo

Risposte:


20

AGGIORNAMENTO: os.system è sconsigliato, sebbene ancora disponibile in Python 3.


Usa os.system:

os.system(my_cmd)

Se vuoi davvero usare subprocess, ecco la soluzione (per lo più estratta dalla documentazione per subprocess):

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

OTOH, puoi evitare completamente le chiamate di sistema:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)

1
Funziona, ma lascia che ti chieda: qual è lo scopo della libreria dei processi secondari se os.system ha già fatto il lavoro? Ho la sensazione che avrei dovuto usare invece sottoprocesso poiché è una libreria dedicata a questo compito, anche se questa volta lo sto facendo solo per me stesso, questa volta starò bene usando os.system.
catatemypythoncode

La libreria dei sottoprocessi è molto più flessibile di os.system, e può modellare con os.systemprecisione, ma è anche più complessa con cui lavorare.
Marcelo Cantos

13
os.systemè venuto prima subprocess. La prima è un'API legacy che la seconda intende sostituire.
Babbo Natale

5
@catatemypythoncode: non dovresti usare os.system()o shell=True. Per reindirizzare l'output di un sottoprocesso, utilizzare il stdoutparametro come mostrato nella risposta di Ryan Thompson . Anche se non hai bisogno di un sottoprocesso ( cat) nel tuo caso, potresti concatenare i file usando Python puro.
jfs

4
OTOH = D'altra parte
Cephlin

271

In Python 3.5+ per reindirizzare l'output, basta passare un handle di file aperto per l' stdoutargomento a 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)

Come altri hanno sottolineato, l'utilizzo di un comando esterno come catper questo scopo è del tutto estraneo.


9
Questa dovrebbe essere la risposta alla domanda generale sulle tubazioni quando si utilizza la shell di Python
Kaushik Ghose

46
Questa è la risposta corretta, non quella contrassegnata come corretta.
Justin Blake

7
Per Python 3.5+ usa subprocess.run(my_cmd, stdout=outfile)che sta sostituendosubprocess.call(...)
Austin Yates

1
Da notare, questo non funziona con gli oggetti file personalizzati, se non hanno un campo fileno (se non sono un file reale.)
Eliezer Miron

1
Poiché Python <3.5 è deprecato al momento, ho aggiornato la risposta con il tuo commento, @AustinYates.
Greg Dubicki

5

@PoltoS Voglio unire alcuni file e quindi elaborare il file risultante. Pensavo che usare il gatto fosse l'alternativa più semplice. C'è un modo migliore / pitonico per farlo?

Ovviamente:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())

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

Quando usi un sottoprocesso , il processo deve essere terminato.Questo è un esempio.Se non uccidi il processo, il file sarà vuoto e non potrai leggere nulla.Può essere eseguito su Windows .Non posso assicurarmi che possa eseguito su Unix.


1
Si tratta di un esempio di codice cattivo (non funziona su Unix, ma dimostra cattive pratiche for line in .readlines():, s +=) e proc.kill()può portare a perdita di informazioni in generale (non permettere al sottoprocesso per terminare con grazia (su Unix) - contenuto unflushed è perduto ). Comunque, la nota sul buffering è più appropriata come commento.
jfs

Lo eseguo su Windows è OK (perché kill è uguale a terminare su Windows). Su Unix potrebbe essere necessario utilizzare proc.terminate (). @ JF Sebastian Non ho il sistema Unix sul mio computer.
Wyx

Se siete su Windows poi cadere shlex.split(), goccia shell=True, goccia >file, goccia open(), ecc e l'uso stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]anziché. Ecco un esempio completo . Altre soluzioni: smettere di leggere l'output del processo in Python senza bloccarsi? Nota: nella domanda non è richiesto di terminare manualmente il processo figlio: è possibile risolvere altri problemi, ad esempio un processo potrebbe comportarsi in modo diverso se il suo stdout è un tty ma è fuori tema.
jfs

0

Un caso interessante potrebbe essere l'aggiornamento di un file aggiungendovi un file simile. Quindi non sarebbe necessario creare un nuovo file nel processo. È particolarmente utile nel caso in cui sia necessario aggiungere un file di grandi dimensioni. Ecco una possibilità utilizzando la riga di comando teminal direttamente da python.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.