¿Cómo redirigir la salida con subproceso en Python?


97

Qué hago en la línea de comando:

cat file1 file2 file3 > myfile

Lo que quiero hacer 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

La ejecución de un comando de este tipo en un subproceso no le daría ningún resultado. ¿Puede ser que desee ejecutarlo sin > myfile redirigir la salida de cat file1 file2 file3 a python?
PoltoS

@PoltoS Quiero unir algunos archivos y luego procesar el archivo resultante. Pensé que usar cat era la alternativa más fácil. ¿Hay una forma mejor / pitónica de hacerlo?
catatemypythoncode

os.sendfile()-basada en la solución es posible, consulte Reproducir el comando unix cat en python
jfs

1
Creo que la redirección de salida ('>' o '>>') no funciona en subproceso.Popen (al menos en Python 2.7) (en shell = modo True) En este ejemplo, como señalan otros, puede solucionar esto al no usar la redirección, pero en otros casos la redirección es útil. Si la redirección o la canalización no son compatibles en el subproceso, Popen se debe documentar (y / o os.system () no debe quedar obsoleto hasta que se solucione)
Ribo

Respuestas:


20

ACTUALIZACIÓN: se desaconseja os.system, aunque todavía está disponible en Python 3.


Utilizar os.system:

os.system(my_cmd)

Si realmente desea utilizar el subproceso, aquí está la solución (en su mayoría extraída de la documentación del subproceso):

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

OTOH, puede evitar las llamadas al sistema por completo:

import shutil

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

1
Funciona, pero déjeme preguntarle entonces: ¿Cuál es el punto de la biblioteca de subprocesos si os.system ya hace el trabajo? Tengo la sensación de que debería haber estado usando subprocess en su lugar, ya que es una biblioteca dedicada a esta tarea, aunque como estoy haciendo esto solo para mí, esta vez estaré bien usando os.system.
catatemypythoncode

La biblioteca de subprocesos es mucho más flexible os.systemy puede modelar con os.systemprecisión, pero también es más complejo trabajar con ella.
Marcelo Cantos

13
os.systemvino antes subprocess. La primera es una API heredada que la segunda pretende reemplazar.
Santa

5
@catatemypythoncode: no debes usar os.system()o shell=True. Para redirigir la salida de un subproceso, use el stdoutparámetro como se muestra en la respuesta de Ryan Thompson . Aunque no necesita un subproceso ( cat) en su caso, puede concatenar archivos usando Python puro.
jfs

4
OTOH = Por otro lado
Cephlin

271

En Python 3.5+ para redirigir la salida, simplemente pase un identificador de archivo abierto para el stdoutargumento 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)

Como han señalado otros, el uso de un comando externo como catpara este propósito es completamente extraño.


9
Esta debería ser la respuesta para la pregunta general de la tubería cuando se usa el shell de Python
Kaushik Ghose

46
Esta es la respuesta correcta, no la marcada como correcta.
Justin Blake

7
Para el uso de Python 3.5+ subprocess.run(my_cmd, stdout=outfile)que está reemplazandosubprocess.call(...)
Austin Yates

1
Es de destacar que esto no funciona con objetos de archivo personalizados, si no tienen un campo fileno (si no son un archivo real).
Eliezer Miron

1
Como Python <3.5 está obsoleto a partir de ahora, he actualizado la respuesta con su comentario, @AustinYates.
Greg Dubicki

5

@PoltoS Quiero unir algunos archivos y luego procesar el archivo resultante. Pensé que usar cat era la alternativa más fácil. ¿Hay una forma mejor / pitónica de hacerlo?

Por supuesto:

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

Cuando usa subproceso , el proceso debe ser eliminado. Este es un ejemplo. Si no termina el proceso, el archivo estará vacío y no podrá leer nada. Puede ejecutarse en Windows . No puedo asegurarme de que pueda ejecutar en Unix.


1
Es un ejemplo de código malo (que no funciona en Unix, sino que demuestra las malas prácticas for line in .readlines():, s +=) y proc.kill()puede conducir a la pérdida de información en general (no permitir que el subproceso termina correctamente (en Unix) - unflushed contenido se pierde ). De todos modos, la nota sobre el almacenamiento en búfer es más apropiada como comentario.
jfs

Lo ejecuto en Windows está bien (porque matar es igual a terminar en Windows). En Unix puede ser que deba usar proc.terminate (). @ JF Sebastian No tengo el sistema Unix en mi computadora.
wyx

Si está en Windows luego dejar caer shlex.split(), gota shell=True, gota >file, gota open(), etc y el uso stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]en lugar. Aquí tienes un ejemplo completo . Más soluciones: ¿ Dejar de leer la salida del proceso en Python sin bloquearse? Nota: no hay ningún requisito en la pregunta de que deba terminar el proceso hijo manualmente; podría abordar otros problemas, por ejemplo, un proceso puede comportarse de manera diferente si su salida estándar es un tty pero está fuera de tema.
jfs

0

Un caso interesante sería actualizar un archivo añadiéndole un archivo similar. Entonces uno no tendría que crear un nuevo archivo en el proceso. Es particularmente útil en el caso de que sea necesario agregar un archivo grande. Aquí hay una posibilidad usando la línea de comando teminal directamente desde python.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.