Überprüfen Sie mithilfe der Shell, ob die Datenbank in PostgreSQL vorhanden ist

129

Ich habe mich gefragt, ob mir jemand sagen kann, ob es möglich ist, mithilfe der Shell zu überprüfen, ob eine PostgreSQL-Datenbank vorhanden ist.

Ich erstelle ein Shell-Skript und möchte, dass es die Datenbank nur erstellt, wenn es noch nicht vorhanden ist, aber bisher nicht sehen konnte, wie es implementiert wird.

Jimmy
quelle

Antworten:

199

Ich verwende die folgende Modifikation der Arturo-Lösung:

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


Was es macht

psql -l gibt ungefähr Folgendes aus:

                                        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)

Die Verwendung des naiven Ansatzes bedeutet, dass die Suche nach einer Datenbank mit den Namen "Liste", "Zugriff" oder "Zeilen" erfolgreich ist. Daher leiten wir diese Ausgabe durch eine Reihe integrierter Befehlszeilentools, um nur in der ersten Spalte zu suchen.


Die -tFlagge entfernt Kopf- und Fußzeilen:

 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

Das nächste Bit cut -d \| -f 1teilt die Ausgabe durch das vertikale Pipe- |Zeichen (das mit einem Backslash aus der Shell entfernt wurde) und wählt Feld 1 aus. Dies lässt Folgendes übrig:

 my_db             
 postgres          
 template0         

 template1         

grep -wstimmt mit ganzen Wörtern überein und stimmt daher nicht überein, wenn Sie tempin diesem Szenario suchen . Die -qOption unterdrückt alle Ausgaben, die auf den Bildschirm geschrieben werden. Wenn Sie diese also interaktiv an einer Eingabeaufforderung ausführen möchten, können Sie die ausschließen, -qdamit sofort etwas angezeigt wird.

Beachten Sie, dass dies grep -wmit alphanumerischen Zeichen, Ziffern und dem Unterstrich übereinstimmt. Dies ist genau der Zeichensatz, der in nicht zitierten Datenbanknamen in postgresql zulässig ist (Bindestriche sind in nicht zitierten Bezeichnern nicht zulässig). Wenn Sie andere Zeichen verwenden, grep -wfunktioniert dies nicht für Sie.


Der Exit-Status dieser gesamten Pipeline lautet 0(Erfolg), wenn die Datenbank vorhanden ist, oder 1(Fehler), wenn dies nicht der Fall ist. Ihre Shell setzt die spezielle Variable $?auf den Exit-Status des letzten Befehls. Sie können den Status auch direkt in einer Bedingung testen:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi
Kibibu
quelle
8
Sie können auch hinzufügen ... | grep 0, dass der Shell-Rückgabewert 0 ist, wenn die Datenbank nicht vorhanden ist, und 1, wenn dies der Fall ist. oder ... | grep 1für das gegenteilige Verhalten
acjay
2
@ acjohnson55 noch besser: lass das wcganz fallen. Siehe meine Überarbeitung. (Wenn Sie den Exit - Status umkehren wollen, unterstützt Bash einen Knall Operator: ! psql ...)
benesch
Gerade jetzt sehen, schön
vol7ron
1
Neben anderen Vorschlägen, den wcBefehl fallen zu lassen , würde ich verwenden grep -qw <term>. Dies führt dazu, dass die Shell zurückkehrt, 0wenn eine Übereinstimmung vorliegt und 1anderweitig. Enthält $?dann den Rückgabewert und Sie können diesen verwenden, um zu entscheiden, was als nächstes zu tun ist. Daher empfehle ich, wcin diesem Fall nicht zu verwenden . grepwird tun, was Sie brauchen.
Matt Friedman
Ich habe diese Antwort aufgrund Ihres Feedbacks aktualisiert. Vielen Dank an alle.
Kibibu
80

Der folgende Shell-Code scheint für mich zu funktionieren:

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
quelle
1
Ich mag es, dass Sie keine externen Cut Grep WC und so weiterleiten. Sie prüfen, ob DB vorhanden ist, was vermutlich bedeutet, dass Sie mindestens psql haben, und das ist der geringste und einzige Befehl, den Sie verwenden! wirklich sehr nett. Außerdem erwähnte das Thema weder den Shell-Typ noch die Befehlsversion oder die Distribution. Ich würde niemals eine solche Fülle von Rohren an Systemwerkzeuge weitergeben, die ich bei den anderen Antworten gesehen habe, um das zu wissen. Es führt zu Jahren später Ausgaben
Riccardo Manfrin
1
Ich stimme @RiccardoManfrin zu, dies scheint die direktere Lösung zu sein.
Travis
Wenn Sie dies mit nicht Postgres Benutzer ausführen müssen , können Sie -U Benutzer hinzufügen , aber eine Datenbank auflisten müssen, um eine Verbindung zu, da keine vorhanden sind können Sie die Postgres verwenden können template1 Datenbank dass immer vorhanden ist : psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
jan
In cygwin psql fügt seltsame Steuerzeichen an den Ausgang ( ‚1 \ C-M‘) und man muss prüfen , ob die Ausgabe beginnt erst mit 1:if [[ $(...) == 1* ]]
Januar
27
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Dies gibt 1 zurück, wenn die angegebene Datenbank vorhanden ist, oder 0, wenn dies nicht der Fall ist.

Wenn Sie versuchen, eine bereits vorhandene Datenbank zu erstellen, gibt postgresql eine Fehlermeldung wie die folgende zurück:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
Arturo
quelle
10
Der erste Vorschlag ist sehr gefährlich. Was würde passieren exact_dbname_testwürde existieren? Die einzige Möglichkeit zum Testen besteht darin, eine Verbindung herzustellen.
Wildplasser
6
Diese Antwort ist nicht robust! Es werden Zahlen ungleich Null gedruckt (nicht zurückgegeben!), Wenn Ihr Suchbegriff in einer anderen Spalte angezeigt wird. In der Antwort von Kibibu finden Sie eine korrektere Vorgehensweise.
Acjay
1
"grep -w foo" kann zu Fehlalarmen führen, wenn eine Datenbank mit dem Namen "foo-bar" vorhanden ist. Ganz zu schweigen davon, dass alle Wörter im psql-Ausgabekopf gefunden werden.
Marius Gedminas
1
Ich bin mit dieser Antwort überhaupt nicht einverstanden. Es ist IMMER wahr, wenn Sie diesen Ausdruck in einer logischen Anweisung verwenden. Sie können diese Beispiele versuchen, um zu testen: 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
Was ist mit all dem Schneiden? Wenn Sie sicherstellen möchten, dass Sie nur die erste Spalte betrachten, geben Sie sie einfach in den regulären Ausdruck: ein psql -l | grep '^ exact_dbname\b', der einen Exit-Code festlegt, wenn er nicht gefunden wird.
Steve Bennett
21

Ich bin neu in postgresql, aber mit dem folgenden Befehl habe ich überprüft, ob eine Datenbank vorhanden ist

if psql ${DB_NAME} -c '\q' 2>&1; then
   echo "database ${DB_NAME} exists"
fi
Bruce
quelle
9
Kann weiter vereinfacht werden psql ${DB_NAME} -c ''.
Pedro Romano
2
Sieht für mich gut aus, obwohl es möglicherweise falsch negativ ist, wenn die Datenbank vorhanden ist, aber Sie keine Verbindung herstellen können (Dauerwellen vielleicht?)
Steve Bennett
7
@SteveBennett, wenn Sie keine Berechtigungen für die erforderliche Datenbank haben, dann existiert sie nicht für Sie :)
Viacheslav Dobromyslov
9

Mit dieser Methode können Sie eine Datenbank erstellen, falls diese noch nicht vorhanden ist:

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

Ich kombiniere die anderen Antworten zu einer prägnanten und POSIX-kompatiblen Form:

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

Eine Rückgabe von true( 0) bedeutet, dass es existiert.

Wenn Sie den Verdacht haben, dass Ihr Datenbankname ein nicht standardmäßiges Zeichen hat, wie z. B. $, benötigen Sie einen etwas längeren Ansatz:

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

Die Optionen -tund -Astellen sicher, dass die Ausgabe roh und nicht "tabellarisch" oder mit Leerzeichen aufgefüllte Ausgabe ist. Spalten werden durch das Pipe-Zeichen getrennt |, sodass entweder das cutoder das grepdies erkennen muss. Die erste Spalte enthält den Datenbanknamen.

BEARBEITEN: grep mit -x, um teilweise Namensübereinstimmungen zu verhindern.

Otheus
quelle
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
quelle
+1 Für die kausale sporadische Verwendung würde ich mich für die andere Antwort entscheiden, aber für ein Routineskript ist dies sauberer und robuster. Vorsichtsmaßnahme: Überprüfen Sie, ob der Benutzer 'postgres' ohne Passwort eine Verbindung herstellen kann.
Leonbloy
Ja, es liegt ein Problem mit dem benötigten Benutzernamen vor. OTOH: Sie möchten keine andere Rolle ohne Verbindungsberechtigung verwenden.
Wildplasser
3

Der Vollständigkeit halber eine andere Version, die Regex anstelle von Schnurschneiden verwendet:

psql -l | grep '^ exact_dbname\b'

Also zum Beispiel:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi
Steve Bennett
quelle
Die Verwendung \bhat das gleiche Problem wie alle Antworten, bei grep -wdenen Datenbanknamen Zeichen enthalten können, die keine Wortbestandteile enthalten, -und daher stimmen fooauch Übereinstimmungsversuche überein foo-bar.
Phils
2

Die akzeptierte Antwort von kibibu ist insofern fehlerhaft, als grep -wsie mit jedem Namen übereinstimmt , der das angegebene Muster als Wortkomponente enthält.

dh wenn Sie nach "foo" suchen, dann ist "foo-backup" eine Übereinstimmung.

Otheus 'Antwort bietet einige gute Verbesserungen, und die Kurzversion funktioniert in den meisten Fällen korrekt, aber die längere der beiden angebotenen Varianten weist ein ähnliches Problem mit passenden Teilzeichenfolgen auf.

Um dieses Problem zu beheben, können wir das POSIX- -xArgument verwenden, um nur die gesamte Übereinstimmung zu erzielen Textzeilen abzugleichen.

Aufbauend auf Otheus 'Antwort sieht die neue Version folgendermaßen aus:

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

Trotzdem bin ich geneigt zu sagen, dass die Antwort von Nicolas Grilly - bei der Sie tatsächlich Postgres nach der spezifischen Datenbank fragen - der beste Ansatz von allen ist.

Phils
quelle
2

Die anderen Lösungen (die fantastisch sind) übersehen die Tatsache, dass psql eine Minute oder länger warten kann, bevor eine Zeitüberschreitung auftritt, wenn keine Verbindung zu einem Host hergestellt werden kann. Ich mag diese Lösung, bei der das Zeitlimit auf 3 Sekunden festgelegt wird:

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

Dies dient zum Herstellen einer Verbindung zu einer Entwicklungsdatenbank auf dem offiziellen Postgres Alpine Docker-Image.

Wenn Sie Rails verwenden und eine Datenbank einrichten möchten, falls diese noch nicht vorhanden ist (wie beim Starten eines Docker-Containers), funktioniert dies gut, da Migrationen idempotent sind:

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

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

kürzere Version

Justin
quelle
0

Ich bin immer noch ziemlich unerfahren mit Shell-Programmierung. Wenn dies aus irgendeinem Grund wirklich falsch ist, stimmen Sie mich ab, aber seien Sie nicht zu beunruhigt.

Aufbauend auf Kibibus Antwort:

# 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
quelle