Python – ¿Cómo paso una cadena en subprocess.Popen (usando el argumento estándar)?

Si hago lo siguiente:

import subprocess from cStringIO import StringIO subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0] 

Yo obtengo:

 Traceback (most recent call last): File "", line 1, in ? File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__ (p2cread, p2cwrite, File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles p2cread = stdin.fileno() AttributeError: 'cStringIO.StringI' object has no attribute 'fileno' 

Al parecer, un objeto cStringIO.StringIO no grapa lo suficientemente cerca de un archivo duck para adaptarse a subprocess.Popen. ¿Cómo trabajo alrededor de esto?

Popen.communicate() :

Tenga en cuenta que si desea enviar datos a la entrada estándar del proceso, debe crear el objeto Popen con stdin = PIPE. De manera similar, para obtener algo que no sea Ninguno en la tupla de resultados, también debe proporcionar stdout = PIPE y / o stderr = PIPE.

Reemplazo de os.popen *

  pipe = os.popen(cmd, 'w', bufsize) # ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin 

Advertencia Use se comunique () en lugar de stdin.write (), stdout.read () o stderr.read () para evitar interlockings debido a que cualquiera de los otros búferes de tuberías del sistema operativo se llene y bloquee el proceso secundario.

Entonces tu ejemplo podría ser escrito de la siguiente manera:

 from subprocess import Popen, PIPE, STDOUT p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT) grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0] print(grep_stdout.decode()) # -> four # -> five # -> 

En la versión actual de Python 3, puede usar subprocess.run , para pasar la entrada como una cadena a un comando externo y obtener su estado de salida, y su salida como una cadena en una llamada:

 #!/usr/bin/env python3 from subprocess import run, PIPE p = run(['grep', 'f'], stdout=PIPE, input='one\ntwo\nthree\nfour\nfive\nsix\n', encoding='ascii') print(p.returncode) # -> 0 print(p.stdout) # -> four # -> five # -> 

Me di cuenta de esta solución:

 >>> p = subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=subprocess.PIPE) >>> p.stdin.write(b'one\ntwo\nthree\nfour\nfive\nsix\n') #expects a bytes type object >>> p.communicate()[0] 'four\nfive\n' >>> p.stdin.close() 

Hay alguno mejor?

Estoy un poco sorprendido de que nadie haya sugerido crear una tubería, que en mi opinión es la forma más sencilla de pasar una cadena a un subproceso:

 read, write = os.pipe() os.write(write, "stdin input here") os.close(write) subprocess.check_call(['your-command'], stdin=read) 

Hay una solución hermosa si estás usando Python 3.4 o mejor. Use el argumento de input en lugar del argumento stdin , que acepta un argumento de bytes:

 output = subprocess.check_output( ["sed", "s/foo/bar/"], input=b"foo", ) 

Estoy usando python3 y descubrí que necesitas codificar tu cadena antes de que puedas pasarla a stdin:

 p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=PIPE) out, err = p.communicate(input='one\ntwo\nthree\nfour\nfive\nsix\n'.encode()) print(out) 

“Aparentemente, un objeto cStringIO.StringIO no se curra lo suficientemente cerca de un archivo pato para adaptarse a subprocess.Popen”

🙂

Me temo que no. La tubería es un concepto de sistema operativo de bajo nivel, por lo que absolutamente requiere un objeto de archivo representado por un descriptor de archivo de nivel de sistema operativo. Su solución es la correcta.

 from subprocess import Popen, PIPE from tempfile import SpooledTemporaryFile as tempfile f = tempfile() f.write('one\ntwo\nthree\nfour\nfive\nsix\n') f.seek(0) print Popen(['/bin/grep','f'],stdout=PIPE,stdin=f).stdout.read() f.close() 
 """ Ex: Dialog (2-way) with a Popen() """ p = subprocess.Popen('Your Command Here', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=PIPE, shell=True, bufsize=0) p.stdin.write('START\n') out = p.stdout.readline() while out: line = out line = line.rstrip("\n") if "WHATEVER1" in line: pr = 1 p.stdin.write('DO 1\n') out = p.stdout.readline() continue if "WHATEVER2" in line: pr = 2 p.stdin.write('DO 2\n') out = p.stdout.readline() continue """ .......... """ out = p.stdout.readline() p.wait() 

Tenga en cuenta que Popen.communicate(input=s) puede dar problemas si s es demasiado grande, ya que aparentemente el proceso principal lo almacenará antes de iniciar el subproceso secundario, lo que significa que necesita “el doble” de memoria utilizada en ese momento (al menos De acuerdo con la explicación “debajo del capó” y la documentación relacionada que se encuentra aquí ). En mi caso particular, s fue un generador que primero se expandió completamente y luego se escribió en stdin por lo que el proceso principal fue enorme justo antes de que se generara el niño, y no se dejó memoria para ahorcarlo:

File "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py", line 1130, in _execute_child self.pid = os.fork() OSError: [Errno 12] Cannot allocate memory

 p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT) p.stdin.write('one\n') time.sleep(0.5) p.stdin.write('two\n') time.sleep(0.5) p.stdin.write('three\n') time.sleep(0.5) testresult = p.communicate()[0] time.sleep(0.5) print(testresult)