subprocess.check_output未正确识别环境变量问题咨询
这个问题其实是踩了subprocess中shell=True时参数传递的隐蔽小坑,我来给你拆解清楚:
首先看你第二个命令的结果,你理解的没错——没加shell=True时,echo是被直接调用的系统程序,它会把$DUMMY当成普通字符串输出,所以得到b'$DUMMY\n'完全合理。
重点说第一个命令为什么失效:当你设置shell=True并使用列表形式传递参数时,subprocess的处理逻辑和你想的不一样。在Unix类系统下,这行代码:
subprocess.check_output(["echo", "$DUMMY"], env={"DUMMY" : "321"}, shell=True)
实际上等价于让系统执行:
sh -c "echo" "$DUMMY"
这里shell把列表的第一个元素echo作为要执行的命令字符串,第二个元素$DUMMY则变成了shell的位置参数(也就是$1),但echo命令本身没有接收任何参数,所以输出空行;而你设置的环境变量DUMMY根本没被shell展开——因为$DUMMY是作为参数传给shell的,不是在shell要执行的命令字符串里。
要让环境变量被正确展开,有两种靠谱的解决方式:
方式1:用单字符串传递完整命令(注意风险)
把整个命令写成一个字符串传给check_output,这样shell会完整解析这个字符串,包括环境变量的展开:
subprocess.check_output("echo $DUMMY", env={"DUMMY": "321"}, shell=True)
执行这个会得到你预期的b'321\n'。不过要注意:这种方式存在命令注入风险,如果你的参数来自不可信的外部输入,绝对不要用这种写法。
方式2:保持列表参数,正确构造命令字符串(更安全)
如果想继续用列表参数(更安全,能避免注入),需要把要执行的完整命令作为列表的唯一元素,让shell去解析整个命令字符串:
subprocess.check_output(["echo $DUMMY"], env={"DUMMY": "321"}, shell=True)
这里列表里只有一个元素"echo $DUMMY",shell会解析这个字符串,自然就能正确展开$DUMMY了。
另外还有一种更严谨的写法,用位置参数传递值,完全避免环境变量的依赖:
subprocess.check_output(["echo $1", "321"], shell=True)
这种方式不需要设置env,直接把值作为shell的位置参数传入,同样能得到正确结果。
内容的提问来源于stack exchange,提问作者Bendik

