Python 3.4.3 subprocess.Popen obtener salida de comando sin canalización?

Estoy tratando de asignar la salida de un comando a una variable sin que el comando piense que se está canalizando. La razón de esto es que el comando en cuestión da un texto sin formato como salida si se está canalizando, pero da un texto con formato de color si se está ejecutando desde el terminal. Necesito obtener este texto con formato de color.

Hasta ahora he probado algunas cosas. He probado a Popen así:

output = subprocess.Popen(command, stdout=subprocess.PIPE) output = output.communicate()[0] output = output.decode() print(output) 

Esto me permitirá imprimir la salida, pero me da la salida sin formato que recibo cuando se canaliza el comando. Eso tiene sentido, ya que lo estoy introduciendo aquí en el código Python. Pero tengo curiosidad por saber si hay una manera de asignar la salida de este comando, directamente a una variable, sin que el comando ejecute la versión canalizada de sí mismo.

También he probado la siguiente versión que se basa en check_output en su lugar:

 output = subprocess.check_output(command) output = output.decode() print(output) 

Y nuevamente obtengo la misma salida sin formato que devuelve el comando cuando se canaliza el comando.

    ¿Hay alguna forma de obtener la salida con formato, la salida que el comando normalmente daría desde el terminal, cuando no se canaliza?

    Utilizando pexpect :

    2.py:

     import sys if sys.stdout.isatty(): print('hello') else: print('goodbye') 

    subproceso

     import subprocess p = subprocess.Popen( ['python3.4', '2.py'], stdout=subprocess.PIPE ) print(p.stdout.read()) --output:-- goodbye 

    pexpect

     import pexpect child = pexpect.spawn('python3.4 2.py') child.expect(pexpect.EOF) print(child.before) #Print all the output before the expectation. --output:-- hello 

    Aquí está con grep --colour=auto :

     import subprocess p = subprocess.Popen( ['grep', '--colour=auto', 'hello', 'data.txt'], stdout=subprocess.PIPE ) print(p.stdout.read()) import pexpect child = pexpect.spawn('grep --colour=auto hello data.txt') child.expect(pexpect.EOF) print(child.before) --output:-- b'hello world\n' b'\x1b[01;31mhello\x1b[00m world\r\n' 

    Sí, puedes usar el módulo pty .

     >>> import subprocess >>> p = subprocess.Popen(["ls", "--color=auto"], stdout=subprocess.PIPE) >>> p.communicate()[0] # Output does not appear in colour 

    Con pty :

     import subprocess import pty import os master, slave = pty.openpty() p = subprocess.Popen(["ls", "--color=auto"], stdout=slave) p.communicate() print(os.read(master, 100)) # Print 100 bytes # Prints with colour formatting info 

    Nota de la documentación:

    Debido a que el manejo de pseudo-terminales es altamente dependiente de la plataforma, existe un código para hacerlo solo para Linux. (Se supone que el código de Linux funciona en otras plataformas, pero aún no se ha probado).

    Una manera menos que hermosa de leer toda la salida hasta el final de una sola vez:

     def num_bytes_readable(fd): import array import fcntl import termios buf = array.array('i', [0]) if fcntl.ioctl(fd, termios.FIONREAD, buf, 1) == -1: raise Exception("We really should have had data") return buf[0] print(os.read(master, num_bytes_readable(master))) 

    Edición: mejor manera de obtener el contenido a la vez gracias a @Antti Haapala:

     os.close(slave) f = os.fdopen(master) print(f.read()) 

    Edit: la gente tiene razón al señalar que esto se interrumpirá si el proceso genera una gran producción, por lo que la respuesta de @Antti Haapala es mejor.

    Un ejemplo de trabajo políglota (funciona igual para Python 2 y Python 3), usando pty .

     import subprocess import pty import os import sys master, slave = pty.openpty() # direct stderr also to the pty! process = subprocess.Popen( ['ls', '-al', '--color=auto'], stdout=slave, stderr=subprocess.STDOUT ) # close the slave descriptor! otherwise we will # hang forever waiting for input os.close(slave) def reader(fd): try: while True: buffer = os.read(fd, 1024) if not buffer: return yield buffer # Unfortunately with a pty, an # IOError will be thrown at EOF # On Python 2, OSError will be thrown instead. except (IOError, OSError) as e: pass # read chunks (yields bytes) for i in reader(master): # and write them to stdout file descriptor os.write(1, b'' + i + b'') 

    Muchos progtwigs desactivan automáticamente los códigos de impresión en color cuando detectan que no están conectados directamente a un terminal. Muchos progtwigs tendrán una bandera para que puedas forzar la salida de color. Usted podría agregar esta bandera a su llamada de proceso. Por ejemplo:

     grep "search term" inputfile.txt # prints colour to the terminal in most OSes grep "search term" inputfile.txt | less # output goes to less rather than terminal, so colour is turned off grep "search term" inputfile.txt --color | less # forces colour output even when not connected to terminal 

    Pero ten cuidado. La salida de color real es hecha por el terminal. El terminal interpreta los códigos de espacio de caracteres especiales y cambia el color del texto y el color de fondo en consecuencia. Sin el terminal para interpretar los códigos de color, solo verá el texto en negro con estos códigos de escape entremezclados.