Sprawdź, czy baza danych istnieje w PostgreSQL za pomocą powłoki

134

Zastanawiałem się, czy ktoś mógłby mi powiedzieć, czy można użyć powłoki, aby sprawdzić, czy istnieje baza danych PostgreSQL?

Tworzę skrypt powłoki i chcę, aby utworzył bazę danych tylko wtedy, gdy jeszcze nie istnieje, ale do tej pory nie mogłem zobaczyć, jak ją zaimplementować.

Jimmy
źródło

Odpowiedzi:

206

Stosuję następującą modyfikację rozwiązania Arturo:

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


Co to robi

psql -l wyświetla coś podobnego do następującego:

                                        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)

Korzystanie z naiwnego podejścia oznacza, że ​​wyszukiwanie bazy danych o nazwie „Lista,„ Dostęp ”lub„ wiersze ”zakończy się sukcesem. Dlatego przesyłamy te dane wyjściowe za pomocą zestawu wbudowanych narzędzi wiersza poleceń, aby wyszukiwać tylko w pierwszej kolumnie.


-tFlag usuwa nagłówków i stopek:

 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

Następny bit cut -d \| -f 1dzieli dane wyjściowe za pomocą pionowej kreski |(uciekającej z powłoki z ukośnikiem odwrotnym) i wybiera pole 1. To pozostawia:

 my_db             
 postgres          
 template0         

 template1         

grep -wdopasowuje całe słowa, więc nie będzie pasować, jeśli szukasz tempw tym scenariuszu. Ta -qopcja blokuje wszelkie dane wyjściowe zapisywane na ekranie, więc jeśli chcesz uruchomić to interaktywnie w wierszu polecenia, możesz wykluczyć to, -qaby coś zostało natychmiast wyświetlone.

Zwróć uwagę, że grep -wdopasowuje znaki alfanumeryczne, cyfry i podkreślenie, co jest dokładnie zestawem znaków dozwolonym w niecytowanych nazwach baz danych w postgresql (łączniki nie są dozwolone w niecytowanych identyfikatorach). Jeśli używasz innych znaków, grep -wnie zadziała.


Status wyjścia tego całego potoku będzie 0(powodzenie), jeśli baza danych istnieje lub 1(błąd), jeśli tak nie jest. Twoja powłoka ustawi specjalną zmienną $?na kod zakończenia ostatniego polecenia. Możesz również sprawdzić status bezpośrednio w warunku:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi
kibibu
źródło
8
Możesz również dodać, ... | grep 0aby wartość zwracana przez powłokę wynosiła 0, jeśli baza danych nie istnieje, i 1, jeśli tak; lub ... | grep 1dla odwrotnego zachowania
acjay
2
@ acjohnson55 jeszcze lepiej: porzuć wccałkowicie. Zobacz moją wersję. (Jeśli chcesz, aby odwrócić stan wyjścia, Bash obsługuje operator Bang: ! psql ...)
Benesch
Właśnie teraz to widzę, fajnie
vol7ron
1
Oprócz innych sugestii, aby porzucić wcpolecenie, użyłbym grep -qw <term>. Spowoduje to, że powłoka powróci, 0jeśli istnieje dopasowanie lub w 1przeciwnym razie. Następnie $?będzie zawierał wartość zwracaną i możesz na jej podstawie zdecydować, co dalej. Dlatego polecam nie używać wcw tym przypadku. grepzrobi to, czego potrzebujesz.
Matt Friedman,
Zacząłem aktualizować tę odpowiedź na podstawie Twojej opinii. Dziękuje wszystkim.
kibibu
88

Wydaje się, że działa dla mnie następujący kod powłoki:

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
źródło
1
Podoba mi się, że nie korzystasz z żadnego zewnętrznego cut grep wc i innych rzeczy ... sprawdzasz istnienie db, co przypuszczalnie oznacza, że ​​masz przynajmniej psql, a to najmniej i jedyne polecenie, którego używasz! naprawdę bardzo ładnie. Poza tym temat nie wspomniał o typie powłoki, wersji komend czy dystrybucji. Nigdy nie oparłbym takiej ilości potoków na oprzyrządowaniu systemowym, które widziałem w innych odpowiedziach, aby to wiedzieć. Prowadzi to do problemów po latach
Riccardo Manfrin
1
Zgadzam się z @RiccardoManfrin, to wydaje się być bardziej bezpośrednim rozwiązaniem.
Travis
Jeśli chcesz to zrobić z użytkownikiem innym niż postgres, możesz dodać użytkownika -U, ale musisz wyświetlić bazę danych do połączenia, ponieważ żadna nie może istnieć, możesz użyć bazy danych Postgres template1, która zawsze istnieje: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
sty
W cygwin psql dodaje dziwne znaki sterujące do wyjścia ('1 \ C-M') i trzeba sprawdzić, czy wyjście zaczyna się tylko od 1:if [[ $(...) == 1* ]]
sty
29
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Zwróci to 1, jeśli określona baza danych istnieje lub 0 w przeciwnym razie.

Ponadto, jeśli spróbujesz utworzyć bazę danych, która już istnieje, postgresql zwróci komunikat o błędzie podobny do tego:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
Arturo
źródło
10
Pierwsza sugestia jest bardzo niebezpieczna. Co by się stało, exact_dbname_testgdyby istniało? Jedynym sposobem przetestowania jest próba połączenia się z nim.
wildplasser
6
Ta odpowiedź nie jest solidna! Wyświetla (nie zwraca!) Niezerowe liczby, jeśli wyszukiwany termin pojawia się w innej kolumnie. Zapoznaj się z odpowiedzią kibibu, aby uzyskać bardziej poprawny sposób zrobienia tego.
acjay
1
„grep -w foo” może dać fałszywe alarmy, gdy istnieje baza danych o nazwie „foo-bar”. Nie wspominając o tym, że znajdzie wszystkie słowa w nagłówku wyjściowym psql.
Marius Gedminas
1
zdecydowanie nie zgadzam się z tą odpowiedzią. ZAWSZE będzie prawdziwe, jeśli użyjesz tego wyrażenia w instrukcji logicznej. możesz wypróbować te przykłady, aby przetestować: 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
O co chodzi z tym cięciem? Jeśli chcesz się upewnić, że patrzysz tylko na pierwszą kolumnę, po prostu umieść ją w wyrażeniu regularnym:, psql -l | grep '^ exact_dbname\b'które ustawia kod zakończenia, jeśli nie zostanie znaleziony.
Steve Bennett
21

Jestem nowy w Postgresql, ale poniższe polecenie jest tym, czego użyłem do sprawdzenia, czy baza danych istnieje

if psql ${DB_NAME} -c '\q' 2>&1; then
   echo "database ${DB_NAME} exists"
fi
bruce
źródło
9
Można dodatkowo uprościć do psql ${DB_NAME} -c ''.
Pedro Romano,
2
Wygląda dobrze dla mnie, chociaż to może, jeśli baza danych istnieje fałszywie ujemny, ale nie można się z nim połączyć (perms może?)
Steve Bennett
7
@SteveBennett, jeśli nie masz żadnych uprawnień do wymaganej bazy danych, to nie istnieje dla Ciebie :)
Viacheslav Dobromyslov
10

Możesz stworzyć bazę danych, jeśli jeszcze nie istnieje, używając tej metody:

if [[ -z `psql -Atqc '\list mydatabase' postgres` ]]; then createdb mydatabase; fi
Nicolas Grilly
źródło
9

Łączę inne odpowiedzi w zwięzłą i zgodną z POSIX formę:

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

Zwrot true( 0) oznacza, że ​​istnieje.

Jeśli podejrzewasz, że nazwa Twojej bazy danych może mieć niestandardowy znak, taki jak $, potrzebujesz nieco dłuższego podejścia:

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

-tI -Aopcji upewnij się, że wyjście jest surowy, a nie „tabelaryczne” lub wyjście spacje wyściełane. Kolumny są oddzielone przez postać rury |, więc albo cutczy grepma to rozpoznać. Pierwsza kolumna zawiera nazwę bazy danych.

EDYCJA: grep z -x, aby zapobiec dopasowaniu częściowych nazw.

Otheus
źródło
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
źródło
+1 W przypadku sporadycznego, sporadycznego użycia, wybrałbym drugą odpowiedź, ale w przypadku rutynowego skryptu jest to bardziej przejrzyste i niezawodne. Uwaga: sprawdź, czy użytkownik „postgres” może się połączyć bez hasła.
leonbloy
Tak, wystąpił problem z wymaganą nazwą użytkownika. OTOH: nie chciałbyś używać innej roli, która nie ma uprawnień do połączenia.
wildplasser
3

Dla kompletności, inna wersja używająca wyrażenia regularnego zamiast cięcia ciągów:

psql -l | grep '^ exact_dbname\b'

Na przykład:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi
Steve Bennett
źródło
Używanie \bma ten sam problem, co wszystkie odpowiedzi, w grep -wktórych nazwy baz danych mogą zawierać znaki niebędące składnikami słowa, takie jak -i dlatego próby dopasowania foorównież będą pasować foo-bar.
phils
2

zaakceptowana odpowiedź kibibu jest błędna i grep -wbędzie pasować do dowolnej nazwy zawierającej określony wzorzec jako składnik słowa.

tzn. jeśli szukasz "foo", wtedy "foo-backup" jest dopasowaniem.

Odpowiedź Otheusa zapewnia kilka dobrych ulepszeń, a krótka wersja będzie działać poprawnie w większości przypadków, ale dłuższy z dwóch oferowanych wariantów wykazuje podobny problem z dopasowaniem podciągów.

Aby rozwiązać ten problem, możemy użyć -xargumentu POSIX, aby dopasować tylko całe linie tekstu.

Opierając się na odpowiedzi Otheusa, nowa wersja wygląda następująco:

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

To powiedziawszy, jestem skłonny powiedzieć, że odpowiedź Nicolasa Grilly'ego - w której faktycznie pytasz postgres o konkretną bazę danych - jest najlepszym podejściem ze wszystkich.

phils
źródło
2

Inne rozwiązania (które są fantastyczne) pomijają fakt, że psql może odczekać minutę lub dłużej przed upływem limitu czasu, jeśli nie może połączyć się z hostem. Podoba mi się więc to rozwiązanie, które ustawia limit czasu na 3 sekundy:

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

Służy do łączenia się z bazą danych programistycznych na oficjalnym obrazie postgres Alpine Docker.

Osobno, jeśli używasz Railsów i chcesz skonfigurować bazę danych, jeśli jeszcze nie istnieje (jak podczas uruchamiania kontenera Docker), działa to dobrze, ponieważ migracje są idempotentne:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
Dan Kohn
źródło
1

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

krótsza wersja

Justin
źródło
0

Nadal nie mam doświadczenia w programowaniu powłoki, więc jeśli z jakiegoś powodu jest to naprawdę złe, oceń mnie, ale nie przejmuj się zbytnio.

Budowanie na podstawie odpowiedzi 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
źródło
0
  • W jednej linii:

PGPASSWORD=mypassword psql -U postgres@hostname -h postgres.hostname.com -tAc 'select 1' -d dbnae || echo 0

Zwróci to 1, jeśli db istnieje 0, jeśli nie

  • lub bardziej czytelne:
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
abahet
źródło
0

Wyzwalacz podziel przez zero, jeśli nie istnieje, sprawdź kod powrotu w ten sposób:

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
Aaron
źródło