Перевірте, чи існує база даних у PostgreSQL за допомогою оболонки


130

Мені було цікаво, чи зможе хтось розповісти мені про те, чи можна використовувати shell, щоб перевірити, чи існує база даних PostgreSQL?

Я створюю скрипт оболонки, і хочу лише, щоб він створив базу даних, якщо вона ще не існує, але до цього часу не змогла зрозуміти, як її реалізувати.

Відповіді:


199

Я використовую таку модифікацію рішення Arturo:

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


Що це робить

psql -l виводить щось на зразок наступного:

                                        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)

Використання наївного підходу означає, що пошук у базі даних під назвою "Список", "Доступ" чи "рядки" буде успішним. Тому ми передаємо цей вихід через купу вбудованих інструментів командного рядка для пошуку лише в першому стовпці.


-tПрапор видаляє верхні і нижні колонтитули:

 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

Наступний біт cut -d \| -f 1розбиває висновок за |символом вертикальної труби (втекли з оболонки із зворотною косою рисою) і вибирає поле 1. Це залишає:

 my_db             
 postgres          
 template0         

 template1         

grep -wвідповідає цілим словам, і тому вони не збігаються, якщо ви шукаєте tempв цьому сценарії. Ця -qопція пригнічує будь-який вихід, записаний на екран, тому, якщо ви хочете це запустити інтерактивно в командному рядку, ви можете виключити, -qщоб щось відображалося негайно.

Зауважте, що це grep -wзбіги буквено-цифрових, цифр та підкреслення, що є саме набором символів, дозволеним у котируваних іменах баз даних у postgresql (дефіси не є законними в ідентифікованих ідентифікаторах). Якщо ви використовуєте інші символи, grep -wце не буде працювати для вас.


Статус виходу всього цього конвеєра буде 0(успішним), якщо база даних існує, або 1(збій), якщо її немає. Ваша оболонка встановить спеціальну змінну $?до статусу виходу останньої команди. Ви також можете перевірити стан безпосередньо в умовному режимі:

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

8
Ви також можете додати, ... | grep 0щоб значення повернення оболонки було 0, якщо БД не існує та 1, якщо воно є; або ... | grep 1за протилежну поведінку
acjay

2
@ acjohnson55 ще краще: wcповністю скиньте . Дивіться мою редакцію. (Якщо ви хочете , щоб змінити статус виходу, Bash підтримує оператор чубчика: ! psql ...)
Бенеш

Щойно бачимо це, приємно
vol7ron

1
На додаток до інших пропозицій відмовитись від wcкоманди, я б використав grep -qw <term>. Це призведе до повернення оболонки, 0якщо є відповідність та 1інше. Потім $?буде містити повернене значення, і ви можете скористатися цим, щоб вирішити, що робити далі. Тож рекомендую не використовувати wcв цьому випадку. grepзробить те, що потрібно.
Метт Фрідман

Мені доводилося оновлювати цю відповідь на основі ваших відгуків. Дякую усім.
kibibu

81

Наступний код оболонки, здається, працює для мене:

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
Мені подобається, що ви не ретранслируєте жоден зовнішній зрізний грецький wc та інше. дійсно дуже приємно Крім того, тема не згадувала тип оболонки, ані версію команд чи дистрибутив. Я б ніколи не передавав на такому плеторі труб системний інструмент, який я бачив в інших відповідях, щоб знати це. Це призводить до наступних років
Ріккардо

1
Я згоден з @RiccardoManfrin, це здається більш прямим рішенням.
Тревіс

Якщо вам необхідно виконати це з користувачем без Postgres ви можете додати -U користувача, але повинні перерахувати бази даних для підключення, так як ніхто не міг би існувати ви можете використовувати Postgres template1 бази даних , яка завжди існує: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
джан

У Cygwin PSQL додає дивні символи управління на вихід ( «1 \ С-М») і потрібно перевірити , якщо вихід починається тільки з 1:if [[ $(...) == 1* ]]
джан

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

Це поверне 1, якщо вказана база даних існує, або 0 в іншому випадку.

Крім того, якщо ви спробуєте створити базу даних, яка вже існує, postgresql поверне повідомлення про помилку так:

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

10
Перша пропозиція дуже небезпечна. Що б сталося exact_dbname_test, існувало б? Єдиний спосіб тестування - це спроба підключитися до нього.
wildplasser

6
Ця відповідь не є надійною! Він друкує (не повертає!) Ненульові номери, якщо пошуковий термін відображається в іншому стовпці. Будь ласка, дивіться відповідь kibibu для більш правильного способу зробити це.
acjay

1
"grep -w foo" може дати вам помилкові позитиви, коли існує база даних під назвою "foo-bar". Не кажучи вже про те, що ви знайдете всі слова у вихідному заголовку psql.
Маріус Гедмінас

1
я категорично не згоден з цією відповіддю. ЗАВЖДИ це буде правдою, якщо ви використовуєте цей вираз у логічному висловлюванні. ви можете спробувати ці приклади, щоб перевірити: 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"
Майк Ліонс

2
Що з усіма різанням? Якщо ви хочете переконатися, що ви дивитесь лише на перший стовпець, просто покладіть його в регулярний вираз:, psql -l | grep '^ exact_dbname\b'який встановлює вихідний код, якщо його не знайдено.
Стів Беннетт

21

Я новачок у postgresql, але наступна команда - це те, що я перевіряв, чи існує база даних

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

9
Можна спростити далі psql ${DB_NAME} -c ''.
Педро Романо

2
Виглядає добре для мене, хоча це може Псевдонегативний , якщо база даних існує , але ви не можете підключитися до нього (завивка може бути?)
Steve Bennett

7
@SteveBennett, якщо у вас немає дозволів на потрібну БД, то вона для вас не існує :)
В'ячеслав Добромислов

10

Ви можете створити базу даних, якщо вона ще не існує, використовуючи цей метод:

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

9

Я поєдную інші відповіді на стисну та POSIX сумісну форму:

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

Повернення true( 0) означає, що воно існує.

Якщо ви підозрюєте, що ваше ім'я бази даних може мати нестандартний символ, наприклад $, вам потрібен трохи довший підхід:

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

Параметри -tта -Aпараметри переконайтеся, що вихідний показник є необробленим, а не "табличним" або викладеним пробілом. Стовпці поділяються символом труби |, так що або cutабо grepповинен визнати це. Перший стовпець містить ім'я бази даних.

РЕДАКТУВАННЯ: натискання клавіш -x для запобігання часткового збігу імен


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 Для причинного спорадичного використання я б вирішив іншу відповідь, але для рутинного сценарію це більш чисте та надійне. Caveat: перевірте, чи "postgres" користувача не може зв’язатися без пароля.
leonbloy

Так, є проблема з необхідністю використання імені користувача. OTOH: ви не хочете використовувати іншу роль, не маючи дозволу на підключення.
wildplasser

3

Для повноти інша версія, що використовує регулярний вираз, а не обрізку рядків:

psql -l | grep '^ exact_dbname\b'

Так, наприклад:

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

Використання \bмає ту саму проблему, що і всі відповіді, за допомогою grep -wяких імена бази даних можуть містити символи, що не містять слова, як, -отже, спроби зіставлення fooтакож збігаються foo-bar.
філс

2

Прийнята відповідь kibibu є хибною в тому grep -w, що відповідатиме будь-якій назві, що містить зазначений шаблон як компонент слова.

тобто якщо ви шукаєте "foo", то "foo-backup" - це відповідність.

Відповідь Отея забезпечує деякі хороші вдосконалення, і коротка версія буде працювати коректно в більшості випадків, але довший з двох запропонованих варіантів має схожу проблему з відповідними підрядками.

Щоб вирішити цю проблему, ми можемо використовувати -xаргумент POSIX, щоб відповідати лише цілим рядкам тексту.

Спираючись на відповідь Отея, нова версія виглядає приблизно так:

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

З урахуванням всього цього, я схильний сказати, що відповідь Ніколя Гріллі - де ви насправді ставите запитання про конкретну базу даних - найкращий підхід з усіх.


2

Інші рішення (які є фантастичними) пропускають той факт, що psql може зачекати хвилину або більше, перш ніж вимкнути час, якщо він не може підключитися до хоста. Отже, мені подобається таке рішення, яке встановлює час очікування на 3 секунди:

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

Це для підключення до бази даних розвитку на офіційних Postgres Alpine Докер зображення.

Окремо, якщо ви використовуєте Rails та хочете налаштувати базу даних, якщо вона ще не існує (як при запуску контейнера Docker), це працює добре, оскільки міграції ідентичні:

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


0

Я все ще досить недосвідчений в програмуванні оболонок, тому, якщо це чомусь дійсно неправильно, голосуйте мене, але не варто занадто тривожитися.

Побудова з відповіді кібібу:

# 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.