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ć.

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

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

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

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

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

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.


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

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.


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


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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.