Python subprocess传密码执行pg_dump在Ubuntu18+Python3.6环境失效问题
问题原因分析
- Ubuntu 18默认搭载的PostgreSQL客户端版本过低,老版本
pg_dump默认优先从控制终端/dev/tty读取密码,而非标准输入stdin,你通过p.communicate往stdin写入的密码不会被pg_dump识别,自然会弹出终端密码输入提示。Mac、Ubuntu 20环境下的PostgreSQL客户端版本更高,已适配支持从stdin读取密码的逻辑,所以可以正常运行。 - Python 3.6的
subprocess.Popen在shell=True配置下的管道转发逻辑存在差异,你写入stdin的内容实际上是传给了中间启动的shell进程,没有直接递交给pg_dump子进程,老版本的shell管道转发逻辑不完善进一步导致密码传递失败。 - 你的命令中同时使用了shell级别的输出重定向
> {dir}{filename}.dump和Popen(stdout=PIPE)配置,二者存在冲突,Python 3.6及Ubuntu 18自带的bash没有兼容这种冲突场景,进一步打乱了标准输入输出的管道链路。
兼容解决方案
优先使用PostgreSQL官方支持的密码传递方式,完全避免stdin传密码的兼容问题:
- 方案1:通过
PGPASSWORD环境变量传递密码,不需要修改原有命令逻辑,全版本PostgreSQL客户端均支持,示例代码如下:
import os from subprocess import Popen, STDOUT # 拷贝当前进程的环境变量,新增PGPASSWORD配置 env = os.environ.copy() env['PGPASSWORD'] = db_password command = 'pg_dump -U {user} -h localhost {db_name} > {dir}{filename}.dump'.format( user=db_user, db_name=db_name, dir=self._dump_dir, filename=backup_file_name) # 传入自定义环境变量,不需要再往stdin写密码 p = Popen(command, shell=True, env=env, stderr=STDOUT) p.wait()
- 方案2:如果不想用环境变量,也可以把密码写入
~/.pgpass配置文件,修改权限为600即可,pg_dump会自动读取该文件中的密码,不需要在代码中传递密码。
内容的提问来源于stack exchange,提问作者Евгений Губаев
相关产品推荐
相关产品推荐

