Controlla se il database esiste in PostgreSQL usando la shell


130

Mi chiedevo se qualcuno sarebbe stato in grado di dirmi se è possibile usare la shell per verificare se esiste un database PostgreSQL?

Sto realizzando uno script di shell e voglio solo che crei il database se non esiste già, ma finora non sono stato in grado di vedere come implementarlo.

Risposte:


199

Uso la seguente modifica della soluzione di Arturo:

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


Cosa fa

psql -l produce qualcosa di simile al seguente:

                                        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'uso dell'approccio ingenuo significa che la ricerca di un database chiamato "Elenco," Accesso "o" righe "avrà esito positivo, quindi eseguiamo il piping di questo output attraverso una serie di strumenti incorporati della riga di comando per cercare solo nella prima colonna.


La -tbandiera rimuove intestazioni e piè di pagina:

 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

Il bit successivo, cut -d \| -f 1divide l'output per il |carattere di pipe verticale (fuggito dalla shell con una barra rovesciata) e seleziona il campo 1. Questo lascia:

 my_db             
 postgres          
 template0         

 template1         

grep -wcorrisponde a parole intere e quindi non corrisponderà se stai cercando tempin questo scenario. L' -qopzione sopprime qualsiasi output scritto sullo schermo, quindi se si desidera eseguire questo in modo interattivo al prompt dei comandi, è possibile escludere -qcosì che qualcosa venga visualizzato immediatamente.

Si noti che grep -wcorrisponde a caratteri alfanumerici, cifre e carattere di sottolineatura, che è esattamente l'insieme di caratteri consentiti nei nomi di database non quotati in postgresql (i trattini non sono legali negli identificatori non quotati). Se stai usando altri personaggi, grep -wnon funzionerà per te.


Lo stato di uscita dell'intera pipeline sarà 0(successo) se il database esiste o 1(errore) in caso contrario. La shell imposterà la variabile speciale $?sullo stato di uscita dell'ultimo comando. Puoi anche testare lo stato direttamente in un condizionale:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi

8
È inoltre possibile aggiungere ... | grep 0per rendere il valore di ritorno della shell pari a 0 se il DB non esiste e 1 se lo è; o ... | grep 1per il comportamento opposto
acjay

2
@ acjohnson55 ancora meglio: abbandonare del wctutto. Vedi la mia revisione (Se si vuole invertire lo stato di uscita, Bash supporta un operatore di botto: ! psql ...)
Benesch

Proprio ora vedendo questo, bello
vol7ron

1
Oltre ad altri che suggeriscono di abbandonare il wccomando, vorrei usare grep -qw <term>. Questo farà tornare la shell in 0caso di corrispondenza e in caso 1contrario. Quindi, $?conterrà il valore restituito e potrai utilizzarlo per decidere cosa fare dopo. Quindi, consiglio di non utilizzare wcin questo caso. grepfarà ciò di cui hai bisogno.
Matt Friedman,

Ho cercato di aggiornare questa risposta in base al tuo feedback. Ringrazia tutti.
Kibibu,

81

Il seguente codice shell sembra funzionare per me:

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

1
Mi piace il fatto che non si trasmetta alcun wc e roba di taglio grep esterni .. si verifica l'esistenza di db, il che presumibilmente significa che hai almeno psql, form è il minimo e unico comando che usi! davvero molto bello. Inoltre il soggetto non ha menzionato il tipo di shell né la versione dei comandi o la distribuzione .. Non avrei mai trasmesso una tale pletora di pipe agli strumenti di sistema che ho visto sulle altre risposte per saperlo. Conduce a problemi successivi
Riccardo Manfrin,

1
Concordo con @RiccardoManfrin questa sembra la soluzione più diretta.
Travis,

Se è necessario eseguire questa operazione con un utente non postgres, è possibile aggiungere un utente -U, ma è necessario elencare un database a cui connettersi, poiché non esiste nessuno, è possibile utilizzare il database template1 postgres che esiste sempre: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
gennaio

In cygwin psql aggiunge strani caratteri di controllo all'output ('1 \ C-M') e bisogna verificare se l'output inizia solo con 1:if [[ $(...) == 1* ]]
gen

28
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Ciò restituirà 1 se il database specificato esiste o 0 altrimenti.

Inoltre, se si tenta di creare un database già esistente, postgresql restituirà un messaggio di errore come questo:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists

10
Il primo suggerimento è molto pericoloso. Che cosa sarebbe successo exact_dbname_testsarebbe esistito? L'unico modo per testare è provare a connettersi ad esso.
wildplasser,

6
Questa risposta non è robusta! Stampa (non restituisce!) Numeri diversi da zero se il termine di ricerca appare in un'altra colonna. Si prega di consultare la risposta di Kibibu per un modo più corretto di farlo.
Acjay,

1
"grep -w foo" può darti falsi positivi quando esiste un database chiamato "foo-bar". Per non parlare del fatto che troverà tutte le parole nell'intestazione dell'output di psql.
Marius Gedminas,

1
sono fortemente in disaccordo con questa risposta. Sarà SEMPRE vero se usi questa espressione in un'istruzione logica. puoi provare questi esempi per testare: 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
Cosa c'è con tutto il taglio? Se vuoi assicurarti di guardare solo la prima colonna, inseriscilo in regex:, psql -l | grep '^ exact_dbname\b'che imposta un codice di uscita se non trovato.
Steve Bennett,

21

Sono nuovo di postgresql, ma il comando seguente è quello che ho usato per verificare l'esistenza di un database

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

9
Può essere ulteriormente semplificato psql ${DB_NAME} -c ''.
Pedro Romano,

2
Mi sembra buono, anche se potrebbe essere falso negativo se il database esiste ma non è possibile connettersi ad esso (permanentemente forse?)
Steve Bennett,

7
@SteveBennett, se non hai i permessi per il DB richiesto, allora non esiste per te :)
Viacheslav Dobromyslov

10

Puoi creare un database, se non esiste già, usando questo metodo:

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

9

Sto combinando le altre risposte in una forma concisa e compatibile POSIX:

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

Un ritorno di true( 0) significa che esiste.

Se si sospetta che il nome del proprio database possa avere un carattere non standard come $, è necessario un approccio leggermente più lungo:

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

Le opzioni -te -Aassicurano che l'output sia grezzo e non "tabulare" o con riempimento di spazi bianchi. Le colonne sono separate dal carattere pipe |, quindi cuto grepdeve riconoscere questo. La prima colonna contiene il nome del database.

EDIT: grep con -x per evitare corrispondenze di nomi parziali.


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

+1 Per l'uso sporadico causale, opterei per l'altra risposta, ma per uno script di routine, questo è più pulito e robusto. Avvertenza: verificare che l'utente "postgres" possa eseguire la ricerca senza password.
leonbloy,

Sì, c'è un problema con il nome utente necessario. OTOH: non vorrai usare un altro ruolo senza permesso di connessione.
wildplasser,

3

Per completezza, un'altra versione che utilizza regex anziché il taglio delle stringhe:

psql -l | grep '^ exact_dbname\b'

Quindi per esempio:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi

L'utilizzo \bha lo stesso problema di tutte le risposte, grep -wovvero che i nomi di database possono contenere caratteri non costituiti da parole come -e quindi anche i tentativi di abbinamento foocorrisponderanno foo-bar.
phils,

2

la risposta accettata di kibibu è errata in quanto grep -wcorrisponderà a qualsiasi nome contenente il modello specificato come componente word.

cioè se cerchi "pippo" allora "pippo-backup" è una partita.

La risposta di Otheus fornisce alcuni buoni miglioramenti e la versione corta funzionerà correttamente nella maggior parte dei casi, ma la più lunga delle due varianti offerte presenta un problema simile con sottostringhe corrispondenti.

Per risolvere questo problema, possiamo usare l' -xargomento POSIX per far corrispondere solo intere righe del testo.

Basandosi sulla risposta di Otheus, la nuova versione è simile alla seguente:

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

Detto questo, sono propenso a dire che la risposta di Nicolas Grilly - in cui si chiedono effettivamente postgres sul database specifico - è l'approccio migliore di tutti.


2

Le altre soluzioni (che sono fantastiche) mancano del fatto che psql può attendere un minuto o più prima di scadere se non riesce a connettersi a un host. Quindi, mi piace questa soluzione, che imposta il timeout su 3 secondi:

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

Questo è per il collegamento ad un database di sviluppo sull'immagine ufficiale Dockgrer postgres .

Separatamente, se stai usando Rails e vuoi impostare un database se non esiste già (come quando si avvia un contenitore Docker), questo funziona bene, poiché le migrazioni sono idempotenti:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup

1

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

versione più corta


0

Sono ancora abbastanza inesperto con la programmazione della shell, quindi se questo è davvero sbagliato per qualche motivo, votami, ma non essere troppo allarmato.

Basandosi sulla risposta di 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.