使用 shell 检查 PostgreSQL 中是否存在数据库

我想知道是否有人能够告诉我是否可以使用 shell 来检查 PostgreSQL 数据库是否存在?

我正在创建一个 shell 脚本,我只想让它在数据库还不存在的情况下创建数据库,但是到目前为止还不能看到如何实现它。

128457 次浏览
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

如果指定的数据库存在,则返回1,否则返回0。

另外,如果您尝试创建一个已经存在的数据库,postgreql 将返回如下错误消息:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
#!/bin/sh
DB_NAME=hahahahahahaha
psql -U postgres ${DB_NAME} --command="SELECT version();" >/dev/null 2>&1
RESULT=$?
echo DATABASE=${DB_NAME} RESULT=${RESULT}
#

我是 postgreql 的新手,但是下面的命令是我用来检查数据库是否存在的

if psql ${DB_NAME} -c '\q' 2>&1; then
echo "database ${DB_NAME} exists"
fi

注意/更新(2021) : 虽然这个答案 工程,哲学上我同意其他评论,这样做的正确方法是 问问 Postgres

检查其他包含 psql -c--command的答案是否更适合您的用例(例如 尼古拉斯 · 格里利的Nathan Osman 的Bruce 的佩德罗的变种)


我对阿图罗的解决方案进行了如下修改:

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匹配字母数字、数字和下划线,后者正好是后引号中未加引号的数据库名称中允许的字符集(在未加引号的标识符中,连字符是不合法的)。如果您使用的是其他字符,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

下面的 shell 代码似乎适合我:

if [ "$( psql -XtAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" )" = '1' ]
then
echo "Database already exists"
else
echo "Database does not exist"
fi

关于上面给出的 psql标志的快速帮助:

General options:
-c, --command=COMMAND    run only single command (SQL or internal) and exit
-X, --no-psqlrc          do not read startup file (~/.psqlrc)


Output format options:
-A, --no-align           unaligned table output mode
-t, --tuples-only        print rows only

我对 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

为了完整起见,另一个版本使用正则表达式而不是字符串切割:

psql -l | grep '^ exact_dbname\b'

例如:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
echo "Database exists already."
exit
fi

我将其他答案组合成一个简洁且兼容 POSIX 的表单:

psql -lqtA | grep -q "^$DB_NAME|"

返回 true(0)意味着它存在。

如果您怀疑您的数据库名称可能具有非标准字符,比如 $,那么您需要稍微长一点的方法:

psql -lqtA | cut -d\| -f1 | grep -qxF "$DB_NAME"

-t-A选项确保输出是原始的,而不是“表格式”或空格填充的输出。列由管道字符 |分隔,因此 cutgrep必须识别这一点。第一列包含数据库名称。

编辑: grep with-x 以防止部分名称匹配。

如果数据库不存在,您可以使用以下方法创建数据库:

if [[ -z `psql -Atqc '\list mydatabase' postgres` ]]; then createdb mydatabase; fi

Kibibu 的 接受的答案是有缺陷的,因为 grep -w将匹配包含指定模式的 任何名称作为单词组件。

例如,如果你寻找“ foo”,那么“ foo 备份”是一个匹配。

Otheus 的答案 提供了一些很好的改进,而且短版本在大多数情况下都能正常工作,但是提供的两个变体中较长的一个会出现与匹配子字符串类似的问题。

为了解决这个问题,我们可以使用 POSIX -x参数仅匹配文本的 完整的行。

基于 Otheus 的回答,新版本看起来像这样:

psql -U "$USER" -lqtA | cut -d\| -f1 | grep -qFx "$DBNAME"

尽管如此,我还是倾向于说 尼古拉斯 · 格里利的回答——实际上你可以向 postgres 询问关于特定数据库的信息——是最好的方法。

psql -l|awk '{print $1}'|grep -w <database>

更短的版本

其他解决方案(非常棒)忽略了一个事实,即如果 psql 无法连接到主机,它可以等待一分钟或更长时间才超时。因此,我喜欢这个解决方案,它将超时时间设置为3秒:

PGCONNECT_TIMEOUT=3 psql development -h db -U postgres -c ""

这是为了连接到官方 Postgres高山码头形象的开发数据库。

另外,如果您正在使用 Rails,并且希望在数据库尚不存在的情况下设置数据库(比如在启动 Docker 容器时) ,这种方法可以很好地工作,因为迁移是幂等的:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
  • 一句话:

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

这个命令将返回称为 DATABASE_NAME: psql -At -U postgres -c "select count(*) from pg_databases where datname = 'DATABASE_NAME';的数据库的数量

那么

if [ "$(psql -At -U postgres -c "select count(*) from pg_databases where datname = 'DATABASE_NAME`;")" -eq 0 ] ; then
# This runs if the DB doesn't exist.
fi