Ubuntu20.04执行PostgreSQL脚本报root角色不存在问题咨询
问题背景
在Ubuntu 20.04系统安装PostgreSQL后,计划通过Shell脚本完成数据库账号、业务库的初始化操作,初始脚本内容如下:
#!/bin/bash main() { local randpass=$(date +%s | sha256sum | base64 | head -c 32) #Add PostGres password file for modifying the database newlines=$(printf ":5432::$USER:$randpass\nlocalhost:5432:*:$USER:$randpass\n127.0.0.1:5432:*:$USER:$randpass\n" ) echo -e "$newlines$(cat $HOME/.pgpass >/dev/null)" > $HOME/.pgpass chmod og-rw $HOME/.pgpass #Create Database User and Table echo "CREATE ROLE \"$USER\" WITH CREATEDB LOGIN NOSUPERUSER NOCREATEROLE PASSWORD '$randpass'" | sudo -u postgres psql echo "ALTER ROLE \"$USER\" WITH PASSWORD '$randpass'" | sudo -u postgres psql dropdb peasoup_$USER createdb peasoup_$USER #Setup the Database to store PEASOUP info source set_env_vars $PEASOUP_HOME/tools/db/pdb_setup.sh } main
脚本中已将USER变量设置为test,执行时以root用户运行脚本,抛出如下错误:
root@xxx:/xxx# ./xxxx_setup.sh CREATE ROLE ALTER ROLE dropdb: error: could not connect to database template1: FATAL: role "root" does not exist createdb: error: could not connect to database template1: FATAL: role "root" does not exist psql: error: FATAL: database "peasoup_test" does not exist psql: error: FATAL: database "peasoup_test" does not exist
核心疑问:创建、修改角色的语句已经通过sudo -u postgres psql切换到postgres用户执行,为何后续dropdb、createdb等命令连接数据库时仍然以root用户身份触发报错?
故障原因
- PostgreSQL自带的客户端工具(
psql、createdb、dropdb等)默认遵循系统用户名同名匹配规则:如果没有通过命令参数、环境变量、连接配置文件显式指定连接使用的数据库角色,客户端会直接取当前执行命令的操作系统用户名作为数据库连接的账号。
前两条创建、修改角色的命令前加了sudo -u postgres,是临时切换到操作系统的postgres用户执行psql,所以这两步可以调用数据库内置的postgres超级用户权限执行成功。后续的dropdb、createdb没有加任何用户切换或账号指定参数,当前运行脚本的系统用户是root,客户端就会默认尝试用root角色连接数据库,而数据库中不存在这个角色,自然抛出对应报错。 - 脚本本身存在两处额外逻辑错误,会放大故障:
.pgpass文件写入逻辑错误:$(cat $HOME/.pgpass >/dev/null)将cat命令的输出重定向到了空设备,根本不会把原有.pgpass的内容拼接进新文件,每次执行都会覆盖原有配置;且写入的认证条目是给$USER(test)用户使用的,脚本以root运行时读取的是root家目录下的.pgpass,完全匹配不到test账号的认证规则。- 执行顺序错误:
source set_env_vars被放在了createdb之后,后续执行的pdb_setup.sh无法提前获取需要的环境变量;且前面createdb执行失败,根本没有创建出peasoup_test库,后续连接该库时必然报库不存在的错误。
修复方案
- 调整执行逻辑顺序,将环境变量加载步骤移到所有数据库操作之前。
- 修正.pgpass文件写入逻辑,保留原有配置内容,避免覆盖。
- 所有数据库操作命令显式指定连接用户和连接地址,要么通过
sudo切换到对应系统用户执行,要么通过-U参数指定数据库账号、-h参数指定连接地址,触发.pgpass的密码匹配规则。 dropdb增加--if-exists参数,避免首次运行时因库不存在中断脚本。
修正后的核心脚本参考:
main() { # 提前加载环境变量 source set_env_vars local randpass=$(date +%s | sha256sum | base64 | head -c 32) local db_user="test" local db_name="peasoup_${db_user}" local pgpass_path="${HOME}/.pgpass" # 正确写入.pgpass,保留原有配置 local newlines=$(printf ":5432::${db_user}:${randpass}\nlocalhost:5432:*:${db_user}:${randpass}\n127.0.0.1:5432:*:${db_user}:${randpass}\n" ) if [ -f "$pgpass_path" ]; then echo -e "${newlines}$(cat $pgpass_path)" > "$pgpass_path" else echo -e "$newlines" > "$pgpass_path" fi chmod og-rw "$pgpass_path" # 用postgres超级用户创建业务账号 echo "CREATE ROLE \"${db_user}\" WITH CREATEDB LOGIN NOSUPERUSER NOCREATEROLE PASSWORD '${randpass}'" | sudo -u postgres psql echo "ALTER ROLE \"${db_user}\" WITH PASSWORD '${randpass}'" | sudo -u postgres psql # 显式指定连接用户和地址,执行删库建库操作 dropdb -h localhost -U "${db_user}" --if-exists "${db_name}" createdb -h localhost -U "${db_user}" "${db_name}" # 执行后续库初始化 $PEASOUP_HOME/tools/db/pdb_setup.sh }
如果不想在每条命令后加-h、-U参数,也可以在脚本开头设置全局环境变量PGHOST=localhost、PGUSER=test,PostgreSQL客户端会自动读取这两个变量作为默认连接参数。
内容的提问来源于stack exchange,提问作者gazile
相关产品推荐
相关产品推荐

