PAYATHON 2026

为什么 subprocess 调用 pg_dump 时会连接到默认数据库?

数据小许

结论

两种调用的行为不同:os.system 会把整段字符串交给 shell 解析,而 subprocess.Popen 在 shell=True 时接收的是参数列表。列表里的 > 和文件路径不会像 shell 命令字符串中的重定向符那样生效。因此,pg_dump 没有收到预期的数据库名 sdtests,输出也没有按预期重定向。

在 POSIX shell 下,Popen 使用 shell=True 并接收序列时,序列首项会作为 shell 命令字符串。原调用的首项是 pg_dump,因此 shell 实际执行的命令相当于 pg_dump;sdtests、> 和文件路径都不会作为该命令的参数或重定向来解析。未指定数据库名时,pg_dump 会尝试使用当前操作系统用户名作为数据库名。错误中的 bret 与这种情况相符。

推荐写法:直接把标准输出写入文件

将 shell 保持为默认值 False,把数据库名作为参数传给 pg_dump,再用 Python 打开目标文件:

import subprocess

command = ["pg_dump", "sdtests"]

with open("/home/bret/dump.custom", "w") as dump_file:
    result = subprocess.run(
        command,
        stdout=dump_file,
        stderr=subprocess.PIPE,
        text=True,
    )

if result.returncode != 0:
    raise RuntimeError(f"pg_dump failed: {result.stderr}")

> 和输出路径不需要放进 command。stdout=dump_file 会将命令的标准输出写入文件,stderr 则保留错误信息供排查。

如果确实需要 shell 重定向

也可以传入一整条 shell 命令字符串:

import subprocess

command = "pg_dump sdtests > /home/bret/dump.custom"
result = subprocess.run(command, shell=True, text=True, capture_output=True)

if result.returncode != 0:
    raise RuntimeError(f"pg_dump failed: {result.stderr}")

这种写法会由 shell 解析 >。如果命令包含变量或外部输入,拼接字符串可能带来 shell 注入风险。能用参数列表和 stdout 文件对象时,优先采用前一种方式。

注意事项

以上解释适用于题目中的 POSIX shell 环境。如果数据库名、连接参数或环境与示例不同,实际连接目标也可能受 pg_dump 参数、环境变量和 PostgreSQL 连接配置影响。示例中的 pg_dump sdtests 会将 sdtests 作为数据库名传入。若仍报连接错误,请确认数据库存在,并检查当前用户是否有相应的连接权限。

备注:内容仅供参考。