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


130

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

我正在制作一个shell脚本,我只希望它创建尚不存在的数据库,但是到目前为止还无法看到如何实现它。

Answers:


199

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

使用幼稚的方法意味着将成功搜索名为“列表,访问”或“行”的数据库,因此我们通过一系列内置命令行工具将此输出通过管道传输,仅在第一列中进行搜索。


-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用垂直竖线|字符(用反斜杠从外壳转义)分隔输出,并选择字段1。这将留下:

 my_db             
 postgres          
 template0         

 template1         

grep -w匹配整个单词,因此如果您temp在这种情况下进行搜索,则不会匹配。该-q选项禁止显示任何写入屏幕的输出,因此,如果要在命令提示符下以交互方式运行此输出,则可能要排除-q这样的内容,以便立即显示某些内容。

请注意,它们grep -w匹配字母数字,数字和下划线,这恰好是postgresql中未引用数据库名称中允许的字符集(在未引用标识符中连字符是合法的)。如果您使用其他字符,grep -w将无法使用。


0如果数据库存在,则整个管道的退出状态将为(成功),否则为1(失败)。您的外壳程序会将特殊变量设置$?为最后一个命令的退出状态。您还可以直接在条件中测试状态:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi

8
... | grep 0如果数据库不存在,您还可以添加使shell返回值为0,如果不存在,则为1;或... | grep 1针对相反的行为
13年

2
@ acjohnson55甚至更好:wc完全删除。看到我的修订。(如果你想扭转退出状态,猛砸支持一声巨响运营商:! psql ...
benesch

刚才看到的很好
vol7ron

1
除了其他建议删除该wc命令的建议外,我将使用grep -qw <term>0如果存在匹配项,1否则将导致外壳返回。然后,$?将包含返回值,您可以使用该值来确定下一步要执行的操作。因此,我建议wc在这种情况下不要使用。grep会做你需要的。
马特·弗里德曼

我根据您的反馈意见来更新此答案。谢谢大家
kibibu

81

以下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

1
我喜欢您不中继任何外部剪切的grep wc和其他东西..您检查数据库是否存在,这意味着您至少有psql,这就是您使用的最少且唯一的命令!确实很好。除此之外,主题没有提到shell类型,命令版本或发行版。.我从来没有在其他答案中看到过如此大量的管道到系统工具。它导致了多年以后的问题
Riccardo Manfrin

1
我同意@RiccardoManfrin,这似乎是更直接的解决方案。
特拉维斯

如果您需要使用非postgres用户执行此操作,则可以添加-U用户,但必须列出要连接的数据库,因为可能不存在,您可以使用始终存在的postgres template1数据库: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
jan

在cygwin中,psql将奇怪的控制字符添加到输出('1 \ C-M')中,需要检查输出是否仅以1开头: if [[ $(...) == 1* ]]
Jan

28
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

10
第一个建议是非常危险的。将会发生什么事exact_dbname_test?测试的唯一方法是尝试连接到它。
wildplasser

6
这个答案不可靠!如果您的搜索词出现在另一列中,它将打印(不返回!)非零数字。请参阅kibibu的答案以获取更正确的方法。
acjay

1
当存在名为“ foo-bar”的数据库时,“ grep -w foo”会给您带来误报。更不用说它将在psql输出标头中找到所有单词。
Marius Gedminas

1
我强烈不同意这个答案。如果您在逻辑语句中使用此表达式,则始终为true。您可以尝试以下示例进行测试: psql -l | grep doesnt_matter_what_you_grep | wc -l && echo "true"vspsql -l | grep it_does_matter_here && echo "only true if grep returns anything"
Mike Lyons

2
一切都在做什么?如果要确保只查看第一列,请将其放在regex:中psql -l | grep '^ exact_dbname\b',如果未找到,它将设置退出代码。
史蒂夫·贝内特

21

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

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

9
可以进一步简化为psql ${DB_NAME} -c ''
Pedro Romano 2014年

2
对我来说看起来不错,尽管如果数据库存在但它可能会假阴性,但您无法连接到它(也许是烫发?)
Steve Bennett 2014年

7
@SteveBennett,如果您对所需的数据库没有任何权限,那么它就不存在:)
Viacheslav Dobromyslov 2014年


9

我将其他答案组合为简洁和POSIX兼容形式:

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

返回true0)表示它存在。

如果您怀疑数据库名称可能包含非标准字符,例如$,则需要稍长一点的方法:

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

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

编辑:带有-x的grep可以防止部分名称匹配。


6
#!/bin/sh
DB_NAME=hahahahahahaha
psql -U postgres ${DB_NAME} --command="SELECT version();" >/dev/null 2>&1
RESULT=$?
echo DATABASE=${DB_NAME} RESULT=${RESULT}
#

+1对于因果关系,我会选择其他答案,但是对于常规脚本来说,它更干净,更健壮。警告:请检查用户“ postgres”是否可以在没有密码的情况下连接。
leonbloy

是的,关于所需的用户名存在问题。OTOH:您不想使用没有连接权限的其他角色。
wildplasser

3

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

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
菲利普斯,2016年

2

kibibu 接受的答案有缺陷,因为它grep -w会匹配包含指定模式作为单词组成部分的任何名称。

即,如果您查找“ foo”,则“ foo-backup”是匹配项。

奥修斯的答案提供了一些良好的改进,并且简短的版本在大多数情况下都能正常工作,但是提供的两个变体中的较长者在匹配子字符串方面也存在类似的问题。

要解决此问题,我们可以使用POSIX -x参数仅匹配文本的行。

以Otheus的答案为基础,新版本如下所示:

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

综上所述,我倾向于说Nicolas Grilly的答案(您实际上在向postgres查询特定数据库)是所有方法中最好的。


2

其他解决方案(非常棒)缺少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


0

我对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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.