Python 3.4.3 subprocess.Popen получить вывод команды без трубопроводов?
Я пытаюсь назначить вывод команды переменной без команды, думающей, что она передается по трубопроводу. Причиной этого является то, что данная команда дает неформатированный текст в качестве вывода, если он передается по каналам, но он придает цветному форматированному тексту, если он запускается с терминала. Мне нужно получить этот отформатированный текст.
До сих пор я пробовал несколько вещей. Я пробовал Popen так:
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)
И снова я получаю тот же неформатированный вывод, который команда возвращает, когда команда передана по протоколу.
Есть ли способ получить отформатированный вывод, вывод, который команда обычно выдает из терминала, когда он не передается по каналам?
Ответы
Ответ 1
Используя 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'
Ответ 2
Да, вы можете использовать pty module.
>>> 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.
Ответ 3
Рабочий пример polyglot (работает одинаково для 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>')
Ответ 4
Многие программы автоматически отключают коды цветной печати, когда обнаруживают, что они не подключены напрямую к терминалу. Многие программы будут иметь флаг, чтобы вы могли принудительно выводить цвет. Вы можете добавить этот флаг к вызову процесса. Например:
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
Будьте осторожны. Фактический цветной вывод выполняется терминалом. Терминал интерпретирует специальные коды espace символов и соответственно изменяет цвет текста и цвет фона. Без того, чтобы терминал интерпретировал цветовые коды, вы просто видите текст в черном, причем эти escape-коды перемежаются повсюду.