ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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


k
kibibu

注意/更新(2021):虽然这个答案有效,但从哲学上讲,我同意其他评论,即正确的方法是询问 Postgres。

检查其中包含 psql -c--command 的其他答案是否更适合您的用例(例如 Nicholas Grilly'sNathan Osman'sbruce'sPedro'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 表示相反的行为
@acjohnson55 更好:完全删除 wc。请参阅我的修订。 (如果要反转退出状态,Bash 支持 bang 运算符:! psql ...
除了其他建议删除 wc 命令之外,我将使用 grep -qw <term>。如果匹配,这将导致 shell 返回 0,否则返回 1。然后,$? 将包含返回值,您可以使用它来决定下一步要做什么。因此,我建议在这种情况下不要使用 wcgrep 将满足您的需求。
我根据您的反馈来更新这个答案。谢谢大家。
谢谢菲尔斯,添加了有关该失败案例的说明
y
yolenoyer

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

我喜欢你不依赖任何外部剪切 grep wc 之类的东西。你检查数据库是否存在,这可能意味着你至少有 psql,ant 那是你使用的最少也是唯一的命令!的确很好。除了主题没有提到外壳类型,也没有提到命令版本或发行版。我永远不会将如此多的管道传递给我在其他答案中看到的系统工具。它会导致多年后的问题
我同意@RiccardoManfrin 这似乎是更直接的解决方案。
如果您需要使用非 postgres 用户执行此操作,您可以添加 -U 用户,但必须列出要连接的数据库,因为可能不存在您可以使用始终存在的 postgres template1 数据库:psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
在 cygwin psql 中添加奇怪的控制字符到输出 ('1\CM') 并且需要检查输出是否仅以 1 开头:if [[ $(...) == 1* ]]
w
wonea
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 会发生什么?唯一的测试方法是尝试连接到它。
这个答案不可靠!如果您的搜索词出现在另一列中,它会打印(不返回!)非零数字。请参阅 kibibu 的答案以获取更正确的方法。
当存在名为“foo-bar”的数据库时,“grep -w foo”可能会给您带来误报。更不用说它会在 psql 输出标题中找到所有单词。
我强烈不同意这个答案。如果您在逻辑语句中使用此表达式,它总是正确的。您可以尝试以下示例进行测试: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',如果未找到,它将设置退出代码。
b
bruce

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

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

可以进一步简化为 psql ${DB_NAME} -c ''
对我来说看起来不错,尽管如果数据库存在但您无法连接到它可能会出现误报(可能是烫发?)
@SteveBennett,如果您对所需数据库没有任何权限,那么它对您不存在:)
N
Nicolas Grilly

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

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

O
Otheus

我将其他答案组合成简洁且 POSIX 兼容的形式:

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

返回 true (0) 表示它存在。

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

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

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

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


w
wildplasser
#!/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”是否可以在没有密码的情况下连接。
是的,需要的用户名存在问题。 OTOH:您不想使用没有连接权限的其他角色。
S
Steve Bennett

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

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
D
Dan Kohn

其他解决方案(非常棒)忽略了这样一个事实,即如果 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

C
Community

kibibu 的 accepted answer 存在缺陷,因为 grep -w 将匹配包含指定模式的 any 名称作为单词组件。

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

Otheus's answer 提供了一些很好的改进,短版本在大多数情况下都可以正常工作,但所提供的两个变体中较长的版本在匹配子字符串方面表现出类似的问题。

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

基于 Otheus 的回答,新版本如下所示:

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

综上所述,我倾向于说Nicolas Grilly's answer——您实际上向 postgres 询问特定数据库的地方——是最好的方法。


J
Justin

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

较短的版本


D
David Winiecki

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

A
Amandasaurus

此命令将返回调用 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

a
abahet

在一行中:

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

A
Aaron

如果不存在则触发除以零,然后检查返回代码,如下所示:

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