Python2.7中subprocess.check_output设shell=True为何会挂起?
问题根因
挂起是shell=True参数和列表格式的cmd搭配使用的错误写法导致的,和输出重定向没有关系:
- 当
shell=True时,subprocess会先启动系统Shell进程,仅将传入cmd的第一个元素作为Shell要执行的命令内容,列表剩余元素会被当做Shell本身的启动参数,而非传递给目标执行程序。 - 你当前传入的
cmd是['python2.7','a.py'],实际等价于在终端执行命令sh -c "python2.7" a.py:Shell只会启动不带任何脚本参数的python2.7进程,直接进入Python交互式REPL环境,持续等待标准输入;而父进程b.py调用check_output时会一直阻塞读取子进程的标准输出,等待子进程退出返回EOF,两边互相等待就形成了永久挂起。 - 你看到的栈追踪卡在
self.stdout.read位置,完全匹配这个故障场景:子进程Python REPL没有退出,读操作永远等不到结束标记。
为什么shell=False可以正常运行
shell=False是subprocess的默认工作模式,这种模式下不会额外启动Shell进程,subprocess会直接将列表第一个元素作为可执行程序路径,列表剩余所有元素依次作为该程序的启动参数,实际执行的命令就是你预期的python2.7 a.py,a.py执行完就会正常退出,不会出现挂起。
修复方案
根据实际需求二选一即可:
- 不需要使用Shell特性(通配符、管道、Shell内置命令等)时,保持
shell=False即可,这也是官方推荐的写法,同时能避免Shell注入安全风险。 - 必须使用
shell=True时,不要给cmd传列表,直接传入完整的命令字符串即可:
这种写法实际等价于执行cmd = 'python2.7 a.py'sh -c "python2.7 a.py",可以正确触发a.py脚本运行。
注:Python2.7的subprocess模块不会对
shell=True+列表cmd的错误用法抛出参数校验错误,属于非常容易踩的隐蔽兼容坑。
内容的提问来源于stack exchange,提问作者wymli
相关产品推荐
相关产品推荐

