使用Python subprocess调用外部进程时如何维持日志输出顺序
问题:如何维持子进程stdout与stderr的输出顺序?
现有脚本
a.sh
#!/bin/bash echo 100 python3 b.py if [ $? -ne 0 ]; then exit 1 fi echo "this will not be printed"
b.py
import subprocess print("101") result = subprocess.run(["bash", "c.sh"], capture_output=True, text=True) print(result.stderr) print(result.stdout) if result.check_returncode: raise Exception("")
c.sh
#!/bin/bash echo 102 >&2 echo 103 echo 104 >&2 exit 1 echo "this will not be printed"
当前执行输出
执行bash a.sh后得到的日志顺序:
100 101 102 104 103 Traceback (most recent call last): File "/Users/abamavad/ssc-github/ssce/cmn-deployment/b.py", line 9, in <module> raise Exception("") Exception
期望输出
希望日志按实际执行顺序输出:
100 101 102 103 104 Traceback (most recent call last): File "/Users/abamavad/ssc-github/ssce/cmn-deployment/b.py", line 9, in <module> raise Exception("") Exception
问题定位
问题出在b.py的这段代码:
result = subprocess.run(["bash", "c.sh"], capture_output=True, text=True) print(result.stderr) print(result.stdout)
这段代码会先把c.sh产生的所有stderr内容一次性打印,再打印所有stdout内容,完全打乱了子进程原本的输出顺序。另外原代码中if result.check_returncode:写法错误,check_returncode是方法,需要调用才会触发返回码检查。
解决方案
要维持原输出顺序,不能分开捕获和打印两个流,以下是两种可行方法:
方法1:合并流后一次性输出
将子进程的stderr合并到stdout,捕获后直接打印,同时正确检查返回码:
import subprocess print("101") # 合并stderr到stdout,捕获所有输出 result = subprocess.run(["bash", "c.sh"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) print(result.stdout) # 调用check_returncode方法检查返回码 try: result.check_returncode() except subprocess.CalledProcessError: raise Exception("")
方法2:实时逐行读取输出
如果需要严格模拟直接执行子进程的实时输出顺序,用Popen实时读取每一行内容:
import subprocess print("101") # 启动子进程,合并stderr到stdout proc = subprocess.Popen(["bash", "c.sh"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) # 实时读取并打印每一行 for line in proc.stdout: print(line, end='') # 等待进程结束并获取返回码 proc.wait() # 判断返回码是否正常 if proc.returncode != 0: raise Exception("")
内容的提问来源于stack exchange,提问作者Amin Ba
相关产品推荐
相关产品推荐

