¿Cómo uso subprocess.Popen para conectar múltiples procesos mediante tuberías?
¿ Cómo ejecuto el siguiente comando de shell usando el subprocess
módulo Python?
echo "input data" | awk -f script.awk | sort > outfile.txt
Los datos de entrada provendrán de una cadena, por lo que en realidad no los necesito echo
. He llegado hasta aquí, ¿alguien puede explicar cómo consigo que pase sort
también?
p_awk = subprocess.Popen(["awk","-f","script.awk"],
stdin=subprocess.PIPE,
stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )
ACTUALIZACIÓN : Tenga en cuenta que, si bien la respuesta aceptada a continuación en realidad no responde la pregunta tal como se hizo, creo que S.Lott tiene razón y es mejor evitar tener que resolver ese problema en primer lugar.
Estarías un poco más feliz con lo siguiente.
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" )
Delegar parte del trabajo al shell. Deje que conecte dos procesos con una tubería.
Sería mucho más feliz reescribiendo 'script.awk' en Python, eliminando awk y la canalización.
Editar . Algunas de las razones para sugerir que awk no ayuda.
[Hay demasiadas razones para responder mediante comentarios].
Awk está agregando un paso sin valor significativo. No hay nada exclusivo en el procesamiento de awk que Python no pueda manejar.
La canalización de awk a sort, para grandes conjuntos de datos, puede mejorar el tiempo de procesamiento transcurrido. Para conjuntos cortos de datos, no tiene ningún beneficio significativo. Es útil realizar una medición rápida
awk >file ; sort file
y unaawk | sort
revelación de la simultaneidad. Con la clasificación, rara vez ayuda porque la clasificación no es un filtro de una sola vez.La simplicidad del procesamiento "Python to sort" (en lugar de "Python to awk to sort") evita que aquí se hagan el tipo exacto de preguntas.
Python, aunque tiene más palabras que awk, también es explícito donde awk tiene ciertas reglas implícitas que son opacas para los novatos y confusas para los no especialistas.
Awk (como el propio script de shell) agrega otro lenguaje de programación más. Si todo esto se puede hacer en un lenguaje (Python), al eliminar el shell y la programación awk se eliminan dos lenguajes de programación, lo que permite que alguien se concentre en las partes de la tarea que producen valor.
En pocas palabras: awk no puede agregar un valor significativo. En este caso, awk es un costo neto; añadió suficiente complejidad como para que fuera necesario hacer esta pregunta. Eliminar awk será una ganancia neta.
Barra laterala | b
Por qué es tan difícil construir un oleoducto ( ).
Cuando se enfrenta al caparazón, a | b
tiene que hacer lo siguiente.
Bifurca un proceso hijo del shell original. Esto eventualmente se convertirá en b.
Construya una tubería de sistema operativo. (no es un subproceso de Python.PIPE), pero una llamada
os.pipe()
que devuelve dos nuevos descriptores de archivos que están conectados a través de un búfer común. En este punto, el proceso tiene stdin, stdout, stderr de su padre, además de un archivo que será "a's stdout" y "b's stdin".Bifurca a un niño. El niño reemplaza su salida estándar con la salida estándar de la nueva a. Ejecutar el
a
proceso.El cierre del niño b reemplaza su entrada estándar con la entrada estándar de la nueva b. Ejecutar el
b
proceso.El niño b espera a que se complete a.
El padre está esperando que se complete b.
Creo que lo anterior se puede usar de forma recursiva para generar a | b | c
, pero implícitamente hay que poner entre paréntesis las canalizaciones largas, tratándolas como si fueran a | (b | c)
.
Dado que Python tiene os.pipe()
y os.exec()
y os.fork()
puedes reemplazar sys.stdin
y sys.stdout
, hay una manera de hacer lo anterior en Python puro. De hecho, es posible que puedas encontrar algunos atajos usando os.pipe()
y subprocess.Popen
.
Sin embargo, es más fácil delegar esa operación al shell.
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)
Para emular una canalización de shell:
from subprocess import check_call
check_call('echo "input data" | a | b > outfile.txt', shell=True)
sin invocar el shell (ver 17.1.4.2. Reemplazo de la canalización del shell ):
#!/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
proporciona algo de azúcar de sintaxis:
#!/usr/bin/env python
from plumbum.cmd import a, b # magic
(a << "input data" | b > "outfile.txt")()
El análogo de:
#!/bin/sh
echo "input data" | awk -f script.awk | sort > outfile.txt
es:
#!/usr/bin/env python
from plumbum.cmd import awk, sort
(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
La respuesta aceptada es eludir la pregunta real. Aquí hay un fragmento que encadena la salida de múltiples procesos: Tenga en cuenta que también imprime el comando de shell (algo) equivalente para que pueda ejecutarlo y asegurarse de que la salida sea correcta.
#!/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)