subprocess.Popen调用Shell有效命令报错,设置shell=True正常的原因解析
问题现象
我有一条可在Shell中正常运行的命令:
<ABS_PATH>/odbc2parquet query --connection-string "Driver={<DRIVER_NAME>};SERVER=<SERVER_IP>,<SERVER_PORT>;DATABASE=<DB_NAME>;UID=<UID>;PWD=<PWD>" <OUTPUT_PATH> "<QUERY>"
但以下Python代码无法正常执行:
import subprocess from typing import List cmd_odbc: List[str] = [ "<ABS_PATH>/odbc2parquet", "query", "--connection-string", '"Driver={<DRIVER_NAME>};SERVER=<SERVER_IP>,<SERVER_PORT>;DATABASE=<DB_NAME>;UID=<UID>;PWD=<PWD>"', "<OUTPUT_PATH>", '"<QUERY>"' ] process_odbc: subprocess.Popen = subprocess.Popen(cmd_odbc) process_odbc.wait()
执行后抛出如下错误:
Error: ODBC emitted an error calling 'SQLDriverConnect': State: IM002, Native error: 0, Message: [unixODBC][Driver Manager]Data source name not found and no default driver specified
而采用以下临时解决方案则可正常运行:
import subprocess from typing import List cmd_odbc: List[str] = [ "<ABS_PATH>/odbc2parquet", "query", "--connection-string", '"Driver={<DRIVER_NAME>};SERVER=<SERVER_IP>,<SERVER_PORT>;DATABASE=<DB_NAME>;UID=<UID>;PWD=<PWD>"', "<OUTPUT_PATH>", '"<QUERY>"' ] process_odbc: subprocess.Popen = subprocess.Popen(" ".join(cmd_odbc), shell=True) process_odbc.wait()
问题咨询
为何第一种代码无法运行而第二种可以?两者的核心差异是什么?
解答
两种写法的核心差异在于是否让系统Shell参与命令解析,以及参数传递时的引号处理逻辑完全不同:
第一种写法(无shell=True,列表传参)
subprocess的列表传参模式会直接将列表中每个元素作为独立参数传递给目标程序,不会经过Shell解析。你给--connection-string和<QUERY>参数额外添加的双引号,会被当作参数内容的一部分直接传递给odbc2parquet。比如实际传递的连接字符串是"Driver={<DRIVER_NAME>};..."(包含首尾双引号),ODBC驱动管理器会把带引号的字符串当作驱动名称,自然找不到对应驱动,从而报错。第二种写法(带shell=True,字符串拼接传参)
当设置shell=True时,subprocess会把拼接后的字符串交给系统Shell处理,逻辑和你在终端直接执行命令完全一致。Shell会识别双引号作为参数边界,去掉引号后将内部真实内容作为参数传递给odbc2parquet,因此能正常识别驱动并执行查询。
正确的列表传参写法
如果想不用shell=True也能正常运行,只需去掉参数外的多余双引号:
import subprocess from typing import List cmd_odbc: List[str] = [ "<ABS_PATH>/odbc2parquet", "query", "--connection-string", 'Driver={<DRIVER_NAME>};SERVER=<SERVER_IP>,<SERVER_PORT>;DATABASE=<DB_NAME>;UID=<UID>;PWD=<PWD>', "<OUTPUT_PATH>", "<QUERY>" ] process_odbc: subprocess.Popen = subprocess.Popen(cmd_odbc) process_odbc.wait()
列表传参模式下,subprocess会自动处理参数中的空格等特殊字符,无需手动添加引号包裹。
内容的提问来源于stack exchange,提问作者GregoirePelegrin
相关产品推荐
相关产品推荐

