Подпроцесс Python 3.4.3. Открыть вывод команды без конвейера?
Я пытаюсь присвоить вывод команды переменной, не думая, что она передается по конвейеру. Причина этого заключается в том, что рассматриваемая команда выдает неформатированный текст в качестве вывода, если он передается по трубопроводу, но она дает текст в цветном формате, если она запускается из терминала. Мне нужно получить этот цвет отформатированный текст.
Пока что я попробовал несколько вещей. Я попробовал Попен так:
output = subprocess.Popen(command, stdout=subprocess.PIPE)
output = output.communicate()[0]
output = output.decode()
print(output)
Это позволит мне напечатать вывод, но это даст мне неформатированный вывод, который я получаю, когда команда передается по конвейеру. Это имеет смысл, так как я описываю это здесь, в коде Python. Но мне любопытно, есть ли способ присвоить выходные данные этой команды непосредственно переменной, без команды, запускающей конвейерную версию самой себя.
Я также попробовал следующую версию, которая вместо этого использует check_output:
output = subprocess.check_output(command)
output = output.decode()
print(output)
И снова я получаю тот же неотформатированный вывод, что команда возвращает, когда команда передается по конвейеру.
Есть ли способ получить форматированный вывод, который обычно выводит команда из терминала, когда он не передается по конвейеру?
4 ответа
Используя pexpect:
2.py:
import sys
if sys.stdout.isatty():
print('hello')
else:
print('goodbye')
подпроцесс:
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
Вот оно с 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'
Да, вы можете использовать модуль pty.
>>> import subprocess
>>> p = subprocess.Popen(["ls", "--color=auto"], stdout=subprocess.PIPE)
>>> p.communicate()[0]
# Output does not appear in colour
С 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
Примечание из документов:
Поскольку обработка псевдо-терминалов сильно зависит от платформы, существует код, который делает это только для Linux. (Предполагается, что код Linux работает на других платформах, но еще не был протестирован.)
Менее красивый способ прочитать весь вывод до конца за один раз:
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)))
Изменить: лучший способ получить контент сразу благодаря @Antti Haapala:
os.close(slave)
f = os.fdopen(master)
print(f.read())
Изменить: люди правы, чтобы указать, что это будет тупик, если процесс генерирует большой результат, поэтому ответ @Antti Haapala лучше.
Рабочий пример полиглота (работает одинаково для Python 2 и Python 3), используя 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'<chunk>' + i + b'</chunk>')
Многие программы автоматически отключают коды цветной печати, когда обнаруживают, что они не подключены напрямую к терминалу. Многие программы будут иметь флаг, чтобы вы могли форсировать цвет. Вы можете добавить этот флаг к вашему вызову процесса. Например:
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
Будьте предупреждены, хотя. Фактический вывод цвета осуществляется терминалом. Терминал интерпретирует специальные пробельные коды символов и соответственно изменяет цвет текста и цвет фона. Без терминала для интерпретации цветовых кодов вы просто увидите текст в черном цвете, в котором эти escape-коды разбросаны по всему.