【发布时间】:2013-01-11 00:54:45
【问题描述】:
我想知道是否有人能告诉我是否可以使用 shell 来检查 PostgreSQL 数据库是否存在?
我正在制作一个 shell 脚本,我只希望它创建数据库,如果它不存在但到目前为止还没有看到如何实现它。
【问题讨论】:
标签: postgresql shell
我想知道是否有人能告诉我是否可以使用 shell 来检查 PostgreSQL 数据库是否存在?
我正在制作一个 shell 脚本,我只希望它创建数据库,如果它不存在但到目前为止还没有看到如何实现它。
【问题讨论】:
标签: postgresql shell
注意/更新(2021 年):虽然此答案有效,但从哲学上讲,我同意其他 cmets 的正确方法是询问 Postgres。
检查包含psql -c 或--command 的其他答案是否更适合您的用例(例如Nicholas Grilly's、Nathan Osman's、bruce's 或Pedro's variant
我使用 Arturo 解决方案的以下修改:
psql -lqt | cut -d \| -f 1 | grep -qw <db_name>
psql -l 输出如下内容:
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+-----------+----------+------------+------------+-----------------------
my_db | my_user | UTF8 | en_US.UTF8 | en_US.UTF8 |
postgres | postgres | LATIN1 | en_US | en_US |
template0 | postgres | LATIN1 | en_US | en_US | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | LATIN1 | en_US | en_US | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows)
使用朴素的方法意味着搜索名为“List”、“Access”或“rows”的数据库会成功。所以我们通过一系列内置命令行工具将这个输出传递给仅在第一列中搜索。
-t 标志删除页眉和页脚:
my_db | my_user | UTF8 | en_US.UTF8 | en_US.UTF8 |
postgres | postgres | LATIN1 | en_US | en_US |
template0 | postgres | LATIN1 | en_US | en_US | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | LATIN1 | en_US | en_US | =c/postgres +
| | | | | postgres=CTc/postgres
下一位,cut -d \| -f 1 通过垂直管道 | 字符(用反斜杠从 shell 中转义)拆分输出,并选择字段 1。这样会留下:
my_db
postgres
template0
template1
grep -w 匹配整个单词,因此如果您在这种情况下搜索temp,则不会匹配。 -q 选项禁止写入屏幕的任何输出,因此如果您想在命令提示符下交互式运行此选项,您可以排除 -q 以便立即显示某些内容。
请注意,grep -w 匹配字母数字、数字和下划线,这正是 postgresql 中不带引号的数据库名称中允许的字符集(连字符在不带引号的标识符中是不合法的)。如果您使用其他字符,grep -w 将不适合您。
如果数据库存在,则整个管道的退出状态将为0(成功),如果不存在,则为1(失败)。您的 shell 会将特殊变量 $? 设置为最后一个命令的退出状态。也可以直接在条件中测试状态:
if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
# database exists
# $? is 0
else
# ruh-roh
# $? is 1
fi
【讨论】:
... | grep 0,使shell返回值如果DB不存在则为0,如果存在则为1;或 ... | grep 1 表示相反的行为
wc。请参阅我的修订。 (如果要反转退出状态,Bash 支持 bang 运算符:! psql ...)
wc 命令外,我将使用grep -qw <term>。如果匹配,这将导致 shell 返回0,否则返回1。然后,$? 将包含返回值,您可以使用它来决定下一步要做什么。所以,在这种情况下,我建议不要使用wc。 grep 会做你需要的。
以下 shell 代码似乎对我有用:
if [ "$( psql -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" )" = '1' ]
then
echo "Database already exists"
else
echo "Database does not exist"
fi
【讨论】:
psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
if [[ $(...) == 1* ]]
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l
如果指定的数据库存在则返回 1,否则返回 0。
另外,如果你尝试创建一个已经存在的数据库,postgresql 会返回如下错误信息:
postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR: database "template1" already exists
【讨论】:
exact_dbname_test 会发生什么?唯一的测试方法是尝试连接它。
psql -l | grep doesnt_matter_what_you_grep | wc -l && echo "true" vs psql -l | grep it_does_matter_here && echo "only true if grep returns anything"
psql -l | grep '^ exact_dbname\b',如果未找到,它将设置退出代码。
我是 postgresql 的新手,但是下面的命令是我用来检查数据库是否存在的命令
if psql ${DB_NAME} -c '\q' 2>&1; then
echo "database ${DB_NAME} exists"
fi
【讨论】:
psql ${DB_NAME} -c ''。
如果数据库不存在,您可以使用以下方法创建数据库:
if [[ -z `psql -Atqc '\list mydatabase' postgres` ]]; then createdb mydatabase; fi
【讨论】:
我将其他答案组合成简洁且 POSIX 兼容的形式:
psql -lqtA | grep -q "^$DB_NAME|"
返回true (0) 表示它存在。
如果您怀疑您的数据库名称可能包含非标准字符,例如 $,则需要稍长一点的方法:
psql -lqtA | cut -d\| -f1 | grep -qxF "$DB_NAME"
-t 和 -A 选项确保输出是原始的,而不是“表格”或空格填充的输出。列由管道字符| 分隔,因此cut 或grep 必须识别这一点。第一列包含数据库名称。
编辑:grep 和 -x 以防止部分名称匹配。
【讨论】:
#!/bin/sh
DB_NAME=hahahahahahaha
psql -U postgres ${DB_NAME} --command="SELECT version();" >/dev/null 2>&1
RESULT=$?
echo DATABASE=${DB_NAME} RESULT=${RESULT}
#
【讨论】:
为了完整起见,使用正则表达式而不是字符串切割的另一个版本:
psql -l | grep '^ exact_dbname\b'
例如:
if psql -l | grep '^ mydatabase\b' > /dev/null ; then
echo "Database exists already."
exit
fi
【讨论】:
\b 与使用grep -w 的所有答案有相同的问题,即数据库名称可以包含非单词组成字符,如-,因此尝试匹配foo 将也匹配foo-bar。
其他解决方案(非常棒)忽略了这样一个事实,即如果 psql 无法连接到主机,它可能会在超时之前等待一分钟或更长时间。所以,我喜欢这个解决方案,它将超时设置为 3 秒:
PGCONNECT_TIMEOUT=3 psql development -h db -U postgres -c ""
这是用于连接到官方postgres Alpine Docker 镜像上的开发数据库。
另外,如果您正在使用 Rails 并且想要设置一个尚不存在的数据库(如启动 Docker 容器时),这很有效,因为迁移是幂等的:
bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
【讨论】:
kibibu 的accepted answer 的缺陷在于grep -w 将匹配包含指定模式的任何名称作为单词组件。
即如果您查找“foo”,则“foo-backup”是匹配项。
Otheus's answer 提供了一些很好的改进,短版本在大多数情况下都可以正常工作,但所提供的两个变体中较长的版本在匹配子字符串方面表现出类似的问题。
要解决此问题,我们可以使用 POSIX -x 参数仅匹配文本的整行行。
基于 Otheus 的回答,新版本如下所示:
psql -U "$USER" -lqtA | cut -d\| -f1 | grep -qFx "$DBNAME"
话虽如此,我倾向于说Nicolas Grilly's answer——你实际上向 postgres 询问特定数据库的地方——是最好的方法。
【讨论】:
psql -l|awk '{print $1}'|grep -w <database>
更短的版本
【讨论】:
我对 shell 编程仍然很缺乏经验,所以如果由于某种原因这确实是错误的,请投我反对票,但不要太惊慌。
根据 kibibu 的回答构建:
# If resulting string is not zero-length (not empty) then...
if [[ ! -z `psql -lqt | cut -d \| -f 1 | grep -w $DB_NAME` ]]; then
echo "Database $DB_NAME exists."
else
echo "No existing databases are named $DB_NAME."
fi
【讨论】:
PGPASSWORD=mypassword psql -U postgres@hostname -h postgres.hostname.com -tAc 'select 1' -d dbnae || echo 0
如果 db 存在则返回 1,如果不存在则返回 0
if [ "$(PGPASSWORD=mypassword psql -U postgres@hostname -h postgres.hostname.com -tAc 'select 1' -d dbnae || echo 0 )" = '1' ]
then
echo "Database already exists"
else
echo "Database does not exist"
fi
【讨论】:
如果不存在则触发除以零,然后检查返回码,如下所示:
sql="SELECT 1/count(*) FROM pg_database WHERE datname='db_name'";
error=$(psql -h host -U user -c "$sql" postgres);
if $error
then
echo "doesn't exist";
else
echo "exists";
fi
【讨论】: