Python 3.4.3 subprocess.Popen获取命令输出而不管道?

时间:2015-03-08 09:20:31

标签: python linux bash 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)

我再次获得与命令管道时命令返回的相同的无格式输出。

有没有办法获取格式化的输出,这个命令通常会从终端提供的输出,当它没有被管道传输时?

4 个答案:

答案 0 :(得分:9)

使用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'

答案 1 :(得分:4)

是的,您可以使用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的答案更好。

答案 2 :(得分:3)

使用pty的工作多语言示例(对于Python 2和Python 3也是如此)。

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>')

答案 3 :(得分:1)

许多程序在检测到它们未直接连接到终端时会自动关闭彩色打印代码。许多程序都会有一个标志,因此您可以强制输出颜色。您可以将此标志添加到您的流程调用中。例如:

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代码并相应地更改文本颜色和背景颜色。如果没有终端解释颜色代码,您将看到黑色文本,这些逃逸代码贯穿始终。