Verifique si la base de datos existe en PostgreSQL usando shell

130

Me preguntaba si alguien podría decirme si es posible usar shell para verificar si existe una base de datos PostgreSQL.

Estoy creando un script de shell y solo quiero que cree la base de datos si aún no existe, pero hasta ahora no he podido ver cómo implementarlo.

Palanqueta
fuente

Respuestas:

199

Utilizo la siguiente modificación de la solución de Arturo:

psql -lqt | cut -d \| -f 1 | grep -qw <db_name>


Que hace

psql -l produce algo como lo siguiente:

                                        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)

Usar el enfoque ingenuo significa que la búsqueda de una base de datos llamada "Lista", "Acceso" o "filas" tendrá éxito. Por lo tanto, canalizamos esta salida a través de un conjunto de herramientas de línea de comando integradas para buscar solo en la primera columna.


La -tbandera elimina encabezados y pies de página:

 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

El siguiente bit, cut -d \| -f 1divide la salida por el |carácter de tubería vertical (escapado del shell con una barra invertida), y selecciona el campo 1. Esto deja:

 my_db             
 postgres          
 template0         

 template1         

grep -wcoincide con palabras completas, por lo que no coincidirá si está buscando tempen este escenario. La -qopción suprime cualquier salida escrita en la pantalla, por lo que si desea ejecutar esto interactivamente en un símbolo del sistema, puede excluirlo -qpara que algo se muestre de inmediato.

Tenga en cuenta que grep -wcoincide con los caracteres alfanuméricos, los dígitos y el guión bajo, que es exactamente el conjunto de caracteres permitidos en los nombres de bases de datos sin comillas en postgresql (los guiones no son legales en los identificadores sin comillas). Si está utilizando otros personajes, grep -wno funcionará para usted.


El estado de salida de toda esta tubería será 0(exitoso) si la base de datos existe o 1(falla) si no existe. Su shell establecerá la variable especial $?en el estado de salida del último comando. También puede probar el estado directamente en un condicional:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi
kibibu
fuente
8
También puede agregar ... | grep 0para hacer que el valor de retorno del shell sea 0 si la base de datos no existe y 1 si existe; o ... | grep 1por el comportamiento opuesto
acjay
2
@ acjohnson55 aún mejor: suelte el wccompleto. Mira mi revisión. (Si desea revertir el estado de salida, Bash soporta un operador de explosión: ! psql ...)
Benesch
Justo ahora viendo esto, agradable
vol7ron 03 de
1
Además de otras sugerencias para soltar el wccomando, usaría grep -qw <term>. Esto hará que el shell regrese 0si hay una coincidencia y de lo 1contrario. Luego, $?contendrá el valor de retorno y puede usarlo para decidir qué hacer a continuación. Por lo tanto, recomiendo no usar wcen este caso. grepHará lo que necesite.
Matt Friedman
Tuve la oportunidad de actualizar esta respuesta en función de sus comentarios. Gracias a todos.
kibibu
81

El siguiente código de shell parece funcionar para mí:

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
Nathan Osman
fuente
1
Me gusta que no transmitas ningún corte externo grep wc y demás ... verificas la existencia de db, lo que supuestamente significa que tienes al menos psql, ¡y ese es el único comando que usas! muy bueno de verdad. Además, el tema no mencionó el tipo de shell ni la versión o distribución de comandos. Nunca transmitiría una plétora de tuberías a las herramientas del sistema que he visto en las otras respuestas por saber eso. Lleva a problemas de años más tarde
Riccardo Manfrin
1
Estoy de acuerdo con @RiccardoManfrin, esta parece ser la solución más directa.
Travis
Si necesita realizar esto con los no postgres usuario puede agregar -u usuario, sino que tienen que enumerar una base de datos para conectarse a, ya que podría existir ninguno puede utilizar las bases de datos postgres template1 que siempre existe: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
enero
En psql cygwin añade extraños caracteres de control a la salida ( '1 \ C-M') y uno necesita para comprobar si la salida sólo se inicia con 1:if [[ $(...) == 1* ]]
enero
28
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Esto devolverá 1 si la base de datos especificada existe o 0 en caso contrario.

Además, si intenta crear una base de datos que ya existe, postgresql devolverá un mensaje de error como este:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
Arturo
fuente
10
La primera sugerencia es muy peligrosa. ¿Qué pasaría de exact_dbname_testexistir? La única forma de probar es intentar conectarse a él.
wildplasser
66
¡Esta respuesta no es robusta! Imprime (¡no devuelve!) Números distintos de cero si su término de búsqueda aparece en otra columna. Vea la respuesta de kibibu para una forma más correcta de hacer esto.
acjay
1
"grep -w foo" puede darle falsos positivos cuando existe una base de datos llamada "foo-bar". Sin mencionar que encontrará todas las palabras en el encabezado de salida psql.
Marius Gedminas
1
Estoy totalmente en desacuerdo con esta respuesta. SIEMPRE será cierto si usa esta expresión en una declaración lógica. puede probar estos ejemplos para probar: 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
¿Qué pasa con todo el corte? Si desea asegurarse de que solo está mirando la primera columna, simplemente colóquela en la expresión regular:, psql -l | grep '^ exact_dbname\b'que establece un código de salida si no se encuentra.
Steve Bennett
21

Soy nuevo en postgresql, pero el siguiente comando es lo que solía verificar si existe una base de datos

if psql ${DB_NAME} -c '\q' 2>&1; then
   echo "database ${DB_NAME} exists"
fi
bruce
fuente
9
Se puede simplificar aún más psql ${DB_NAME} -c ''.
Pedro Romano
2
Me parece bien, aunque puede ser falso negativo si la base de datos existe pero no se puede conectar a ella (¿permisos permanentes?)
Steve Bennett
77
@SteveBennett, si no tiene permisos para la base de datos requerida, entonces no existe para usted :)
Viacheslav Dobromyslov
10

Puede crear una base de datos, si aún no existe, utilizando este método:

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

Estoy combinando las otras respuestas a una forma sucinta y compatible con POSIX:

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

Un retorno de true( 0) significa que existe.

Si sospecha que el nombre de su base de datos puede tener un carácter no estándar como $, necesita un enfoque un poco más largo:

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

Las opciones -ty -Aaseguran que la salida sea sin formato y no "tabular" o con espacios en blanco. Las columnas están separadas por el carácter de la tubería |, por lo que el cuto el greptiene que reconocer esto. La primera columna contiene el nombre de la base de datos.

EDITAR: grep con -x para evitar coincidencias parciales de nombres.

Oteo
fuente
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}
#
wildplasser
fuente
+1 Para uso esporádico causal, optaría por la otra respuesta, pero para un script de rutina, esto es más limpio y robusto. Advertencia: compruebe que el usuario 'postgres' puede conectarse sin contraseña.
leonbloy
Sí, hay un problema sobre el nombre de usuario que se necesita. OTOH: no querrás usar otra función que no tenga permiso de conexión.
wildplasser
3

Para completar, otra versión que usa expresiones regulares en lugar de cortar cadenas:

psql -l | grep '^ exact_dbname\b'

Entonces, por ejemplo:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi
Steve Bennett
fuente
El uso \btiene el mismo problema que todas las respuestas grep -wque usan que es que los nombres de bases de datos pueden contener caracteres que no son constituyentes de palabras -y, por lo tanto, los intentos de coincidencia footambién coincidirán foo-bar.
phils
2

La respuesta aceptada de kibibu es errónea, ya que grep -wcoincidirá con cualquier nombre que contenga el patrón especificado como un componente de palabra.

es decir, si busca "foo", entonces "foo-backup" es una coincidencia.

La respuesta de Otheus proporciona algunas buenas mejoras, y la versión corta funcionará correctamente en la mayoría de los casos, pero la más larga de las dos variantes ofrecidas exhibe un problema similar con las subcadenas coincidentes.

Para resolver este problema, podemos usar el -xargumento POSIX para hacer coincidir solo líneas enteras del texto.

Sobre la base de la respuesta de Otheus, la nueva versión se ve así:

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

Dicho todo esto, me inclino a decir que la respuesta de Nicolas Grilly, donde realmente le preguntas a postgres sobre la base de datos específica, es el mejor enfoque de todos.

phils
fuente
2

Las otras soluciones (que son fantásticas) pierden el hecho de que psql puede esperar un minuto o más antes de que se agote el tiempo de espera si no puede conectarse a un host. Entonces, me gusta esta solución, que establece el tiempo de espera en 3 segundos:

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

Esto es para conectarse a una base de datos de desarrollo en la imagen oficial de Postgres Alpine Docker.

Por separado, si está utilizando Rails y desea configurar una base de datos si aún no existe (como al iniciar un contenedor Docker), esto funciona bien, ya que las migraciones son idempotentes:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
Dan Kohn
fuente
1

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

versión corta

Justin
fuente
0

Todavía no tengo mucha experiencia con la programación de shell, por lo que si esto está realmente mal por alguna razón, desestime mi voto, pero no se alarme demasiado.

Partiendo de la respuesta de 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
David Winiecki
fuente