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

StackOverflow https://stackoverflow.com/questions/163542

  •  03-07-2019
  •  | 
  •  

Pregunta

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]

me sale:

Traceback (most recent call last):
  File "<stdin>", 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 puedo solucionar esto?

¿Fue útil?

Solución

Popen.communicate () documentación:

  

Tenga en cuenta que si desea enviar datos a   el stdin del proceso, debe   crear el objeto Popen con   stdin = TUBO. Del mismo modo, para obtener algo   distinto de Ninguno en la tupla del resultado,   necesita dar stdout = PIPE y / o   stderr = TUBO también.

     

Reemplazar os.popen *

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

Advertencia Utilice comunicarse () en lugar de   stdin.write (), stdout.read () o   stderr.read () para evitar puntos muertos debido   a cualquiera de los otros búferes de tubería del sistema operativo   llenar y bloquear al niño   proceso.

Entonces tu ejemplo podría escribirse 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
# -> 

Otros consejos

Descubrí 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 uno mejor?

Estoy un poco sorprendido de que nadie sugiriera crear una canalización, que en mi opinión es la forma más sencilla de pasar una cadena a una entrada estándar de 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. Utilice el argumento 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 poder 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 oculta lo suficientemente cerca de un archivo duck 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) le puede dar problemas si s es demasiado grande, porque aparentemente el proceso principal lo amortiguará antes de bifurcar el subproceso secundario, lo que significa que necesita " el doble " usé la memoria en ese momento (al menos de acuerdo con la explicación " debajo de la capucha " y la documentación vinculada encontrada aquí ). En mi caso particular, s era un generador que primero se expandió completamente y solo luego se escribió en stdin , por lo que el proceso padre fue enorme justo antes de que se generara el hijo, y no quedó memoria para bifurcarlo:

Archivo " /opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py" ;, línea 1130, en _execute_child     self.pid = os.fork () OSError: [Errno 12] No se puede asignar memoria

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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top