子进程stderr vs sys.stderr

时间:2018-10-12 13:19:34

标签: python stderr

我不确定这个问题是Python还是shell问题。

我有一个Python程序,该程序在命令上使用子进程调用,该命令可以在stderr上发出错误消息。我自己的程序还使用sys.stderr记录错误。这是一个简单的示例,其中的命令(ls * .foobar)失败:

import sys,subprocess

sys.stderr.write("--Hello\n")
try:
    subprocess.check_call("ls *.foobar",shell=True)
except subprocess.CalledProcessError as e:
    sys.stderr.write("Command failed\n")
sys.stderr.write("--Bye\n")

运行此代码时,控制台上的输出(来自stderr)如下:

  

-您好
  ls:无法访问“ * .foobar”:没有这样的文件或目录
  命令失败
  -再见

如果我将stderr重定向到文件(例如,使用python myscript.py 2>日志),则该文件包含以下内容:

  

ls:无法访问“ * .foobar”:没有这样的文件或目录
  -你好
  命令失败
  -再见

是否有一种方法可以保持消息在文件中的顺序(除了在子进程调用中对文件使用stderr的显式重定向之外)?

这个问题类似于一些标准的stdout / stderr问题,但是在这里,所有东西都应该放在stderr上。

1 个答案:

答案 0 :(得分:4)

您需要 flush 数据写入器到stderr描述符:

sys.stderr.write("--Hello\n")
sys.stderr.flush()

stdio连接到终端时是行缓冲的,连接到管道时是使用固定缓冲的。您正在编写一个\n换行符,当连接到终端时会触发刷新,但是如果没有该行刷新,您将无法向stderr写入足够多的字符以在最终刷新之前触发缓冲区刷新当Python退出时。

如果您使用print(..., file=sys.stderr),则可以通过添加print()来告诉flush()发出flush=True呼叫:

print("--Hello", file=sys.stderr, flush=True)

处理此问题的另一种方法是“捕获并释放”子进程的stderr输出:

sys.stderr.write("--Hello\n")
try:
    subprocess.check_call("ls *.foobar", shell=True, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
    sys.stderr.write(e.stderr)
    sys.stderr.write("Command failed\n")
sys.stderr.write("--Bye\n")

stderr=subprocess.PIPE附加项告诉subprocess捕获命令的stderr输出,并且您可以找到该输出作为e.stderr异常的CalledProcessError属性。