打印"time" shell 命令的输出而不会丢失格式



由于某种原因,当我从终端运行"time./"时,我得到的格式是:

real	;0m0.090s用户#x09;0.086秒sys	;0m0.004s

但是当我在Python 2.7.6中执行相同的命令时:

result = subprocess.Popen("time ./<binary>", shell = True, stdout = subprocess.PIPE)

当我print(result.stderr):时,我得到了这种格式

0.09user 0.00system 0:00.09elapsed

有什么方法可以强制使用第一个(real、user、sys)格式吗?

来自man time文档:

实用程序完成后,time将所用的总时间、系统开销所消耗的时间以及用于执行实用程序的时间写入标准错误流

加粗强调mine。您捕获的是stdout流,而不是stderr流,因此无论看到什么输出,都必须是其他东西破坏Python stderr流的结果。

捕获stderr:

proc = subprocess.Popen("time ./<binary>", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()

然后,stderr变量保持time命令输出。

如果这种情况继续产生相同的输出,那么/bin/bash实现有一个内置的time命令,它会覆盖/usr/bin/time版本(可能会在一行中输出所有内容)。您可以通过告诉Python使用以下内容来强制使用bash内置:

proc = subprocess.Popen("time ./<binary>", shell=True, executable='/bin/bash',
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()

首先:Martijn Pieters关于需要捕获timestderr而不是stdout的回答是正确的。

此外,至少在较旧版本的Python(如3.1)上,subprocess.Popen对象包含几个可以被视为"输出"的东西。尝试print只会导致:

<subprocess.Popen object at 0x2068fd0>

如果以后的版本可以使用print,则必须对其内容进行一些处理,可能包括对输出进行篡改。

Popen对象读取(和打印)

Popen对象有一个stderr字段,它是一个可读的、类似文件的对象。您可以像任何其他类似文件的对象一样从中read,尽管不建议这样做。引用粉红色的安全警告:

警告

使用communicate()而不是.stdin.write.stdout.read.stderr.read,以避免由于任何其他操作系统管道缓冲区填满并阻塞子进程而导致的死锁。

要打印Popen的内容,您必须:

  1. communicate()与子进程,将其返回的2元组(stdoutstderr)分配给局部变量。

  2. 将变量的内容转换为字符串——默认情况下,它是一个bytes对象,就好像"文件"是在二进制模式下打开的一样。

下面是一个小程序,它打印shell命令stderr输出,而不进行篡改(不包括从ASCII到Unicode的转换)。

#!/usr/bin/env python3
import subprocess
def main():
result = subprocess.Popen(
'time sleep 0.2',
shell=True,
stderr=subprocess.PIPE,
)
stderr = result.communicate()[1]
stderr_text = stderr.decode('us-ascii').rstrip('n')
#print(stderr_text)  # Prints all lines at once.
# Or, if you want to process output line-by-line...
lines = stderr_text.split('n')
for line in lines:
print(line)
return
if "__main__" == __name__:
main()

此输出在旧的Fedora Linux系统上,运行bashLC_ALL设置为"C":

real#x0009;0m0.201s用户#x0009;0m0.000ssys#x0009;0m0.001s

注意,您需要在脚本的stderr_text = stderr.decode(...)行周围添加一些错误处理。。。据我所知,time根据本地化、环境变量等发出非ASCII字符。

备选方案:universal_newlines

您可以使用universal_newlines选项将一些解码样板保存到Popen。它自动完成从bytesstrings的转换:

如果universal_newlinesTrue,则这些文件对象将使用locale.getpreferredencoding(False)返回的编码以通用换行模式作为文本流打开。[…]

def main_universal_newlines():
result = subprocess.Popen(
'time sleep 0.2',
shell=True,
stderr=subprocess.PIPE,
universal_newlines=True,
)
stderr_text = result.communicate()[1].rstrip('n')
lines = stderr_text.split('n')
for line in lines:
print(line)
return

请注意,我仍然需要手动剥离最后一个'n',以完全匹配shell的输出。

相关内容

  • 没有找到相关文章