Vérifiez si la base de données existe dans PostgreSQL à l'aide du shell

130

Je me demandais si quelqu'un pourrait me dire s'il est possible d'utiliser le shell pour vérifier si une base de données PostgreSQL existe?

Je suis en train de créer un script shell et je veux seulement qu'il crée la base de données si elle n'existe pas déjà mais jusqu'à présent je n'ai pas été en mesure de voir comment l'implémenter.

Jimmy
la source

Réponses:

199

J'utilise la modification suivante de la solution d'Arturo:

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


Ce qu'il fait

psql -l produit quelque chose comme ce qui suit:

                                        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)

L'utilisation de l'approche naïve signifie que la recherche d'une base de données appelée "Liste," Accès "ou" lignes "réussira. Nous acheminons donc cette sortie à travers un ensemble d'outils de ligne de commande intégrés pour rechercher uniquement dans la première colonne.


L' -tindicateur supprime les en-têtes et les pieds de page:

 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

Le bit suivant cut -d \| -f 1divise la sortie par le |caractère de tube vertical (échappé du shell avec une barre oblique inverse), et sélectionne le champ 1. Cela laisse:

 my_db             
 postgres          
 template0         

 template1         

grep -wcorrespond à des mots entiers et ne correspondra donc pas si vous recherchez tempdans ce scénario. L' -qoption supprime toute sortie écrite à l'écran, donc si vous voulez l'exécuter de manière interactive à une invite de commande, vous pouvez avec pour exclure le -qafin que quelque chose soit affiché immédiatement.

Notez qu'il grep -wcorrespond aux caractères alphanumériques, aux chiffres et au trait de soulignement, qui est exactement le jeu de caractères autorisé dans les noms de base de données non entre guillemets dans postgresql (les tirets ne sont pas autorisés dans les identifiants non entre guillemets). Si vous utilisez d'autres personnages, grep -wcela ne fonctionnera pas pour vous.


Le statut de sortie de tout ce pipeline sera 0(succès) si la base de données existe ou 1(échec) si ce n'est pas le cas. Votre shell définira la variable spéciale $?sur l'état de sortie de la dernière commande. Vous pouvez également tester le statut directement dans un conditionnel:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi
kibibu
la source
8
Vous pouvez également ajouter ... | grep 0pour que la valeur de retour du shell soit 0 si le DB n'existe pas et 1 s'il existe; ou ... | grep 1pour le comportement opposé
acjay
2
@ acjohnson55 encore mieux: laissez tomber le wctout. Voir ma révision. (Si vous voulez inverser l'état de sortie, Bash prend en charge un opérateur bang: ! psql ...)
Benesch
Je viens de voir ça, gentil
vol7ron
1
Suite à d'autres suggérant d'abandonner la wccommande, j'utiliserais grep -qw <term>. Cela entraînera le retour du shell 0s'il y a une correspondance et 1sinon. Ensuite, $?contiendra la valeur de retour et vous pourrez l'utiliser pour décider quoi faire ensuite. Donc, je recommande de ne pas utiliser wcdans ce cas. grepfera ce dont vous avez besoin.
Matt Friedman
J'ai mis à jour cette réponse en fonction de vos commentaires. Merci a tous.
kibibu
81

Le code shell suivant semble fonctionner pour moi:

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
la source
1
J'aime que vous ne relayiez sur aucun grep wc externe de coupure et d'autres trucs .. vous vérifiez l'existence de db, ce qui suppose que vous avez au moins psql, fourmi c'est la moindre et seule commande que vous utilisez! très beau effectivement. D'ailleurs le sujet ne mentionnait pas le type de shell ni la version des commandes ou la distribution. Je ne relayerais jamais sur une telle pléthore de tuyaux à l'outillage système que j'ai vu sur les autres réponses pour le savoir. Cela conduit à des problèmes d'années plus tard
Riccardo Manfrin
1
Je suis d'accord avec @RiccardoManfrin, cela semble être la solution la plus directe.
Travis
Si vous devez effectuer ce avec l' utilisateur non postgres vous pouvez ajouter un utilisateur -U, mais ont à la liste une base de données pour se connecter à, comme personne ne pouvait vous existez pouvez utiliser la base de données postgres template1 qui existe toujours: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
jan
Dans cygwin, psql ajoute des caractères de contrôle étranges à la sortie ('1 \ C-M') et il faut vérifier si la sortie ne commence que par 1:if [[ $(...) == 1* ]]
jan
28
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Cela renverra 1 si la base de données spécifiée existe ou 0 sinon.

De plus, si vous essayez de créer une base de données qui existe déjà, postgresql renverra un message d'erreur comme celui-ci:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
Arturo
la source
10
La première suggestion est très dangereuse. Que se passerait-il d' exact_dbname_testexister? La seule façon de tester est d'essayer de s'y connecter.
wildplasser
6
Cette réponse n'est pas robuste! Il imprime (ne renvoie pas!) Des nombres différents de zéro si votre terme de recherche apparaît dans une autre colonne. Veuillez consulter la réponse de kibibu pour une manière plus correcte de procéder.
acjay
1
"grep -w foo" peut vous donner des faux positifs quand une base de données nommée "foo-bar" existe. Sans oublier qu'il trouvera tous les mots dans l'en-tête de sortie psql.
Marius Gedminas
1
Je ne suis pas du tout d'accord avec cette réponse. Ce sera TOUJOURS vrai si vous utilisez cette expression dans une instruction logique. vous pouvez essayer ces exemples pour tester: 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'est-ce que c'est que tout ce découpage? Si vous voulez vous assurer que vous ne regardez que la première colonne, placez-la simplement dans l'expression régulière:, psql -l | grep '^ exact_dbname\b'qui définit un code de sortie s'il n'est pas trouvé.
Steve Bennett
21

Je suis nouveau sur postgresql, mais la commande suivante est ce que j'ai utilisé pour vérifier si une base de données existe

if psql ${DB_NAME} -c '\q' 2>&1; then
   echo "database ${DB_NAME} exists"
fi
Bruce
la source
9
Peut être simplifié par la suite psql ${DB_NAME} -c ''.
Pedro Romano
2
Cela me semble bien, même si cela peut faux négatif si la base de données existe mais que vous ne pouvez pas vous y connecter (perms peut-être?)
Steve Bennett
7
@SteveBennett, si vous n'avez aucune autorisation sur la base de données requise, elle n'existe pas pour vous :)
Viacheslav Dobromyslov
10

Vous pouvez créer une base de données, si elle n'existe pas déjà, en utilisant cette méthode:

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

Je combine les autres réponses dans un formulaire succinct et compatible POSIX:

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

Un retour de true( 0) signifie qu'il existe.

Si vous pensez que le nom de votre base de données pourrait avoir un caractère non standard tel que $, vous avez besoin d'une approche légèrement plus longue:

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

Les options -tet -As'assurent que la sortie est brute et non "tabulaire" ou remplie d'espaces. Les colonnes sont séparées par le caractère pipe |, donc le cutou le grepdoit le reconnaître. La première colonne contient le nom de la base de données.

EDIT: grep avec -x pour éviter les correspondances partielles de nom.

Otheus
la source
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
la source
+1 Pour une utilisation causale sporadique, j'opterais pour l'autre réponse, mais pour un script de routine, c'est plus propre et robuste. Attention: vérifiez que l'utilisateur 'postgres' peut détecter sans mot de passe.
leonbloy
Oui, il y a un problème concernant le nom d'utilisateur requis. OTOH: vous ne voudriez pas utiliser un autre rôle sans autorisation de connexion.
wildplasser
3

Pour être complet, une autre version utilisant regex plutôt que la coupe de chaîne:

psql -l | grep '^ exact_dbname\b'

Donc par exemple:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi
Steve Bennett
la source
L'utilisation \bpose le même problème que toutes les réponses utilisant, grep -wc'est-à-dire que les noms de base de données peuvent contenir des caractères non constitutifs de mots comme -et donc les tentatives de correspondance correspondent fooégalement foo-bar.
phils
2

La réponse acceptée de kibibu est imparfaite car grep -welle correspondra à tout nom contenant le modèle spécifié en tant que composant de mot.

c'est-à-dire que si vous recherchez "foo", "foo-backup" est une correspondance.

La réponse d'Otheus apporte de bonnes améliorations et la version courte fonctionnera correctement dans la plupart des cas, mais la plus longue des deux variantes proposées présente un problème similaire avec les sous-chaînes correspondantes.

Pour résoudre ce problème, nous pouvons utiliser l' -xargument POSIX pour ne faire correspondre que des lignes entières du texte.

S'appuyant sur la réponse d'Otheus, la nouvelle version ressemble à ceci:

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

Cela dit, j'ai tendance à dire que la réponse de Nicolas Grilly - où vous posez en fait des questions à postgres sur la base de données spécifique - est la meilleure approche de toutes.

phils
la source
2

Les autres solutions (qui sont fantastiques) passent à côté du fait que psql peut attendre une minute ou plus avant d'expirer s'il ne peut pas se connecter à un hôte. Donc, j'aime cette solution, qui définit le délai d'expiration à 3 secondes:

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

Ceci permet de se connecter à une base de données de développement sur l'image officielle postgres Alpine Docker.

Séparément, si vous utilisez Rails et que vous souhaitez configurer une base de données si elle n'existe pas déjà (comme lors du lancement d'un conteneur Docker), cela fonctionne bien, car les migrations sont idempotentes:

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

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

version plus courte

Justin
la source
0

Je suis encore assez inexpérimenté avec la programmation shell, donc si c'est vraiment faux pour une raison quelconque, votez-moi, mais ne vous inquiétez pas trop.

Construire à partir de la réponse 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
la source