Проверьте, существует ли база данных в PostgreSQL, используя оболочку

130

Мне было интересно, сможет ли кто-нибудь сказать мне, можно ли использовать оболочку для проверки существования базы данных PostgreSQL?

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

Джимми
источник

Ответы:

199

Я использую следующую модификацию решения Артуро:

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
kibibu
источник
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
Мне нравится, что вы не ретранслируете ни на какой внешний cut grep wc и прочее ... вы проверяете наличие db, что, предположительно, означает, что у вас есть хотя бы psql, ant это наименьшая и единственная команда, которую вы используете! действительно очень красиво. Кроме того, в теме не упоминался ни тип оболочки, ни версия команд, ни дистрибутив. Я бы никогда не использовал такое множество каналов для системных инструментов, которые я видел в других ответах, зная это. Это приводит к проблемам спустя годы
Риккардо
1
Я согласен с @RiccardoManfrin, это кажется более прямым решением.
Travis
Если вам нужно выполнить это с пользователем, не являющимся пользователем postgres, вы можете добавить пользователя -U, но при этом необходимо указать базу данных для подключения, поскольку ее не может быть, вы можете использовать базу данных postgres template1, которая всегда существует: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
jan
В 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
Arturo
источник
10
Первое предложение очень опасно. Что бы случилось с exact_dbname_testсуществующим? Единственный способ тестирования - это попытаться подключиться к нему.
wildplasser
6
Этот ответ ненадежен! Он печатает (не возвращает!) Ненулевые числа, если ваш поисковый запрос появляется в другом столбце. См. Ответ Кибибу, чтобы узнать более правильный способ сделать это.
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должно распознаваться либо символом , либо символом . Первый столбец содержит имя базы данных.

РЕДАКТИРОВАТЬ: grep с -x, чтобы предотвратить частичное совпадение имен.

Otheus
источник
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
источник
+1 Для случайного случайного использования я бы выбрал другой ответ, но для обычного сценария он более чистый и надежный. Предупреждение: убедитесь, что пользователь 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.
phils
2

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

т.е. если вы ищите "foo", тогда "foo-backup" будет подходящим.

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

Чтобы решить эту проблему, мы можем использовать -xаргумент POSIX для сопоставления только целых строк текста.

Основываясь на ответе Отеуса, новая версия выглядит так:

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

С учетом всего сказанного, я склонен сказать, что ответ Николаса Грилли - когда вы фактически спрашиваете postgres о конкретной базе данных - является лучшим подходом из всех.

Phils
источник
2

В других решениях (которые просто фантастичны) упускается из виду тот факт, что psql может подождать минуту или больше, прежде чем истечет время ожидания, если он не может подключиться к хосту. Итак, мне нравится это решение, которое устанавливает тайм-аут на 3 секунды:

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

Это для подключения к базе данных разработки на официальном образе Postgres Alpine Docker.

Отдельно, если вы используете Rails и хотите настроить базу данных, если она еще не существует (например, при запуске контейнера Docker), это хорошо работает, поскольку миграции идемпотентны:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
Дэн Кон
источник
1

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

более короткая версия

Джастин
источник
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
Дэвид Винецки
источник