从Python调用curl时stderr输出与命令行不同,原因是什么?
Python调用外部进程时额外输出进度信息的原因与解决方法
现象对比
命令行直接执行curl
在终端直接对不存在的域名执行curl命令,仅返回错误信息:
$ curl https://doesnoexist.test/ curl: (6) Could not resolve host: doesnoexist.test
Python子进程调用curl
用Python的subprocess调用相同命令并捕获stderr时,输出会包含curl进度指示器的初始内容,之后才是错误信息:
import subprocess proc = subprocess.Popen(['curl', 'https://doesnoexist.test/'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) outs, errs = proc.communicate() print(errs)
执行结果:
$ python curl.py b' % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0curl: (6) Could not resolve host: doesnoexist.test\n'
原因分析
curl这类命令行工具会自动检测输出目标是否为交互式终端(TTY):
- 终端直接执行时,工具识别到输出到TTY,会自动调整输出逻辑(比如curl在解析失败时直接输出错误,不显示进度);
- 被Python的subprocess调用时,stdout/stderr被重定向到管道,工具检测到非TTY环境,会输出完整的进度格式内容,即使进程很快退出,这些进度信息已经写入stderr。
很多命令行工具都有这种行为,比如根据是否为TTY决定是否显示颜色、进度条、简化输出等。
解决方法
针对curl的快速处理
给curl添加-s(silent)参数,关闭所有进度类输出,只保留错误信息:
import subprocess proc = subprocess.Popen(['curl', '-s', 'https://doesnoexist.test/'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) outs, errs = proc.communicate() print(errs.decode('utf-8'))
执行后仅输出错误内容:
curl: (6) Could not resolve host: doesnoexist.test
通用解决方案(适用于所有类似进程)
- 工具参数法:查看目标工具的官方文档,找到关闭冗余输出的专属参数(比如很多工具支持
--quiet、--no-progress等),这是最轻量化的解决方式。 - 伪终端模拟法:使用Python的
pty模块创建伪终端,让被调用进程误以为运行在交互式终端中,输出行为与直接在命令行执行时一致:import subprocess import pty import os # 创建伪终端 master, slave = pty.openpty() proc = subprocess.Popen(['curl', 'https://doesnoexist.test/'], stdout=slave, stderr=slave, stdin=slave) os.close(slave) # 读取并处理输出 output = os.read(master, 1024).decode('utf-8') os.close(master) proc.wait() print(output)
内容的提问来源于stack exchange,提问作者Michal Charemza
相关产品推荐
相关产品推荐

