【问题标题】:Check if database exists in PostgreSQL using shell使用 shell 检查 PostgreSQL 中是否存在数据库
【发布时间】:2013-01-11 00:54:45
【问题描述】:

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

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

【问题讨论】:

    标签: postgresql shell


    【解决方案1】:

    注意/更新(2021 年):虽然此答案有效,但从哲学上讲,我同意其他 cmets 的正确方法是询问 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 &lt;term&gt;。如果匹配,这将导致 shell 返回0,否则返回1。然后,$? 将包含返回值,您可以使用它来决定下一步要做什么。所以,在这种情况下,我建议不要使用wcgrep 会做你需要的。
    • 我根据您的反馈更新了这个答案。谢谢大家。
    • 感谢 phils,添加了有关该失败案例的注释
    【解决方案2】:

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

    【讨论】:

    • 我喜欢你不依赖任何外部剪切 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\C-M') 并且需要检查输出是否仅以 1 开头:if [[ $(...) == 1* ]]
    【解决方案3】:
    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 的答案以了解更正确的方法。
    • "grep -w foo" 当存在名为“foo-bar”的数据库时,可能会给您带来误报。更不用说它会在 psql 输出头中找到所有单词。
    • 我强烈反对这个答案。如果您在逻辑语句中使用此表达式,它将始终为真。你可以试试这些例子来测试:psql -l | grep doesnt_matter_what_you_grep | wc -l &amp;&amp; echo "true" vs psql -l | grep it_does_matter_here &amp;&amp; echo "only true if grep returns anything"
    • 所有的切割是怎么回事?如果您想确保只查看第一列,只需将其放入正则表达式中:psql -l | grep '^ exact_dbname\b',如果未找到,它将设置退出代码。
    【解决方案4】:

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

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

    【讨论】:

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

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

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

    【讨论】:

      【解决方案6】:

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

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

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

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

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

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

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

      【讨论】:

        【解决方案7】:
        #!/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:您不想使用没有连接权限的其他角色。
        【解决方案8】:

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

        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
        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

          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 询问特定数据库的地方——是最好的方法。

          【讨论】:

            【解决方案11】:

            psql -l|awk '{print $1}'|grep -w &lt;database&gt;

            更短的版本

            【讨论】:

              【解决方案12】:

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

              【讨论】:

                【解决方案13】:
                • 一行:

                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
                

                【讨论】:

                  【解决方案14】:

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

                  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
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-06-14
                    • 1970-01-01
                    • 2012-03-22
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-11-02
                    • 1970-01-01
                    相关资源
                    最近更新 更多