bash Python 3.4.3 subprocess.Popen 在没有管道的情况下获取命令输出?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28924910/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 12:30:08  来源:igfitidea点击:

Python 3.4.3 subprocess.Popen get output of command without piping?

pythonlinuxbashsubprocesspopen

提问by nullified

I am trying to assign the output of a command to a variable without the command thinking that it is being piped. The reason for this is that the command in question gives unformatted text as output if it is being piped, but it gives color formatted text if it is being run from the terminal. I need to get this color formatted text.

我试图将命令的输出分配给一个变量,而该命令并不认为它正在被管道传输。这样做的原因是,有问题的命令在通过管道传输时提供未格式化的文本作为输出,但如果它是从终端运行的,则它提供彩色格式的文本。我需要获取这种颜色格式的文本。

So far I've tried a few things. I've tried Popen like so:

到目前为止,我已经尝试了一些事情。我试过这样的 Popen:

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

This will let me print the output, but it gives me the unformatted output that I get when the command is piped. That makes sense, as I'm piping it here in the Python code. But I am curious if there is a way to assign the output of this command, directly to a variable, without the command running the piped version of itself.

这将使我打印输出,但它为我提供了通过管道传输命令时获得的未格式化输出。这是有道理的,因为我在 Python 代码中将它传送到这里。但是我很好奇是否有一种方法可以将这个命令的输出直接分配给一个变量,而无需该命令运行其自身的管道版本。

I have also tried the following version that relies on check_output instead:

我还尝试了以下依赖于 check_output 的版本:

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

And again I get the same unformatted output that the command returns when the command is piped.

我再次得到与命令通过管道传输时命令返回的相同的未格式化输出。

Is there a way to get the formatted output, the output the command would normally give from the terminal, when it is not being piped?

有没有办法获得格式化的输出,命令通常从终端给出的输出,当它没有被管道传输时?

采纳答案by 7stud

Using pexpect:

使用pexpect

2.py:

2.py:

import sys

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

subprocess:

子流程:

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

Here it is with grep --colour=auto:

这是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'

回答by Antti Haapala

A working polyglot example (works the same for Python 2 and Python 3), using pty.

一个有效的多语言示例(适用于 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>')

回答by Andrew Magee

Yes, you can use the pty module.

是的,您可以使用pty 模块

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

With pty:

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

Note from the docs:

文档中的注释:

Because pseudo-terminal handling is highly platform dependent, there is code to do it only for Linux. (The Linux code is supposed to work on other platforms, but hasn't been tested yet.)

因为伪终端处理高度依赖于平台,所以只有在 Linux 上有代码才能做到这一点。(Linux 代码应该可以在其他平台上运行,但尚未经过测试。)

A less than beautiful way of reading the whole output to the end in one go:

一口气读完整个输出的一种不太漂亮的方式:

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

Edit: nicer way of getting the content at once thanks to @Antti Haapala:

编辑:感谢@Antti Haapala,可以更好地立即获取内容:

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

Edit: people are right to point out that this will deadlock if the process generates a large output, so @Antti Haapala's answer is better.

编辑:人们正确地指出,如果该过程产生大量输出,这将导致死锁,因此@Antti Haapala 的答案更好。

回答by Dunes

Many programs automatically turn off colour printing codes when they detect they are not connected directly to a terminal. Many programs will have a flag so you can force colour output. You could add this flag to your process call. For example:

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

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 

Be warned though. The actual colour output is done by the terminal. The terminal interprets special character espace codes and changes the text colour and background color accordingly. Without the terminal to interpret the colour codes you will just see the text in black with these escape codes interspersed throughout.

不过要注意。实际的颜色输出由终端完成。终端解释特殊字符 espace 代码并相应地更改文本颜色和背景颜色。如果没有终端来解释颜色代码,您将只会看到黑色文本,这些转义代码贯穿始终。