Periksa apakah basis data ada di PostgreSQL menggunakan shell

130

Saya bertanya-tanya apakah ada yang bisa memberi tahu saya apakah mungkin menggunakan shell untuk memeriksa apakah ada database PostgreSQL?

Saya membuat skrip shell dan saya hanya ingin membuat database jika belum ada tetapi sampai sekarang belum bisa melihat bagaimana mengimplementasikannya.

Jimmy
sumber

Jawaban:

199

Saya menggunakan modifikasi solusi Arturo berikut:

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


Apa yang dilakukannya

psql -l menghasilkan sesuatu seperti berikut:

                                        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)

Menggunakan pendekatan naif berarti bahwa pencarian basis data yang disebut "Daftar," Akses "atau" baris "akan berhasil. Jadi kami menyalurkan output ini melalui sekelompok alat baris perintah bawaan untuk hanya mencari di kolom pertama.


The -tbendera menghilangkan header dan footer:

 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

Bit berikutnya, cut -d \| -f 1membagi output dengan |karakter pipa vertikal (lolos dari shell dengan backslash), dan memilih bidang 1. Ini meninggalkan:

 my_db             
 postgres          
 template0         

 template1         

grep -wcocok dengan seluruh kata, sehingga tidak akan cocok jika Anda mencari tempdalam skenario ini. The -qpilihan menekan keluaran ditulis ke layar, jadi jika Anda ingin menjalankan ini secara interaktif pada command prompt Anda mungkin dengan mengecualikan -qjadi sesuatu akan ditampilkan segera.

Perhatikan bahwa grep -wcocok dengan alfanumerik, angka, dan garis bawah, yang merupakan set karakter yang diperbolehkan dalam nama basis data yang tidak dikutip dalam postgresql (tanda hubung tidak legal dalam pengidentifikasi yang tidak dikutip). Jika Anda menggunakan karakter lain, grep -wtidak akan berfungsi untuk Anda.


Status keluar dari seluruh pipa ini akan 0(berhasil) jika database ada atau 1(gagal) jika tidak. Shell Anda akan mengatur variabel khusus $?ke status keluar dari perintah terakhir. Anda juga dapat menguji status secara langsung dalam kondisi:

if psql -lqt | cut -d \| -f 1 | grep -qw <db_name>; then
    # database exists
    # $? is 0
else
    # ruh-roh
    # $? is 1
fi
kibibu
sumber
8
Anda juga dapat menambahkan ... | grep 0untuk membuat nilai pengembalian shell menjadi 0 jika DB tidak ada dan 1 jika tidak; atau ... | grep 1untuk perilaku yang berlawanan
acjay
2
@ acjohnson55 lebih baik: jatuhkan wcseluruhnya. Lihat revisi saya. (Jika Anda ingin membalikkan status keluar, Bash mendukung operator Bang: ! psql ...)
BENESCH
Baru saja melihat ini, bagus
vol7ron
1
Lebih jauh ke saran lain untuk menjatuhkan wcperintah, saya akan menggunakan grep -qw <term>. Ini akan menyebabkan shell kembali 0jika ada kecocokan dan 1sebaliknya. Kemudian, $?akan berisi nilai kembali dan Anda dapat menggunakannya untuk memutuskan apa yang harus dilakukan selanjutnya. Jadi, saya sarankan untuk tidak menggunakan wcdalam hal ini. grepakan melakukan apa yang Anda butuhkan.
Matt Friedman
Saya sempat memperbarui jawaban ini berdasarkan umpan balik Anda. Terima kasih semuanya.
kibibu
81

Kode shell berikut ini sepertinya bekerja untuk saya:

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
sumber
1
Saya suka bahwa Anda tidak menyampaikan pada cut grep wc dan hal-hal eksternal .. Anda memeriksa keberadaan db, yang seharusnya berarti Anda memiliki setidaknya psql, semut itu perintah yang paling dan hanya Anda gunakan! memang sangat bagus. Selain itu subjek tidak menyebutkan jenis shell atau versi perintah atau distro .. Saya tidak pernah menyampaikan pada pletora pipa seperti itu ke sistem perkakas yang pernah saya lihat di jawaban lain untuk mengetahui hal itu. Ini mengarah pada masalah bertahun-tahun kemudian
Riccardo Manfrin
1
Saya setuju dengan @RiccardoManfrin ini sepertinya solusi yang lebih langsung.
Travis
Jika Anda perlu melakukan ini dengan non user postgres Anda dapat menambahkan pengguna -U, tapi harus daftar database untuk terhubung ke, karena tidak bisa eksis Anda dapat menggunakan postgres template1 database yang selalu ada: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
Januari
Dalam psql cygwin menambahkan karakter kontrol aneh output ( '1 \ C-M') dan satu kebutuhan untuk memeriksa apakah output hanya dimulai dengan 1:if [[ $(...) == 1* ]]
Januari
28
postgres@desktop:~$ psql -l | grep <exact_dbname> | wc -l

Ini akan mengembalikan 1 jika database yang ditentukan ada atau 0 sebaliknya.

Juga, jika Anda mencoba membuat database yang sudah ada, postgresql akan mengembalikan pesan kesalahan seperti ini:

postgres@desktop:~$ createdb template1
createdb: database creation failed: ERROR:  database "template1" already exists
Arturo
sumber
10
Saran pertama sangat berbahaya. Apa yang akan terjadi exact_dbname_test? Satu-satunya cara pengujian adalah mencoba terhubung ke sana.
wildplasser
6
Jawaban ini tidak kuat! Ini mencetak (tidak mengembalikan!) Nomor bukan nol jika istilah pencarian Anda muncul di kolom lain. Silakan lihat jawaban kibibu untuk cara yang lebih benar untuk melakukan ini.
acjay
1
"grep -w foo" dapat memberikan Anda positif palsu ketika database bernama "foo-bar" ada. Belum lagi itu akan menemukan semua kata di header output psql.
Marius Gedminas
1
Saya sangat tidak setuju dengan jawaban ini. Ini akan SELALU berlaku jika Anda menggunakan ungkapan ini dalam pernyataan logis. Anda dapat mencoba contoh-contoh ini untuk menguji: 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
Ada apa dengan semua pemotongan? Jika Anda ingin memastikan bahwa Anda hanya melihat kolom pertama, cukup letakkan di regex:, psql -l | grep '^ exact_dbname\b'yang menetapkan kode keluar jika tidak ditemukan.
Steve Bennett
21

Saya baru di postgresql, tetapi perintah berikut adalah apa yang saya gunakan untuk memeriksa apakah ada basis data

if psql ${DB_NAME} -c '\q' 2>&1; then
   echo "database ${DB_NAME} exists"
fi
bruce
sumber
9
Dapat disederhanakan lebih lanjut menjadi psql ${DB_NAME} -c ''.
Pedro Romano
2
Terlihat bagus untuk saya, walaupun mungkin false negative jika database ada tetapi Anda tidak dapat terhubung dengannya (perms mungkin?)
Steve Bennett
7
@SteveBennett, jika Anda tidak memiliki izin untuk DB yang diperlukan maka itu tidak ada untuk Anda :)
Viacheslav Dobromyslov
10

Anda dapat membuat database, jika belum ada, menggunakan metode ini:

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

Saya menggabungkan jawaban lain ke formulir ringkas dan POSIX yang kompatibel:

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

Pengembalian true( 0) berarti ada.

Jika Anda mencurigai nama database Anda mungkin memiliki karakter non-standar seperti $, Anda perlu pendekatan yang sedikit lebih lama:

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

Itu -t dan -Apilihan pastikan output baku dan tidak "tabular" atau output spasi-empuk. Kolom dipisahkan oleh karakter pipa |, sehingga harus cutatau grepharus mengenali ini. Kolom pertama berisi nama basis data.

EDIT: grep dengan -x untuk mencegah kecocokan nama parsial.

Otheus
sumber
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
sumber
+1 Untuk penggunaan sebab-akibat sporadis, saya akan memilih jawaban yang lain, tetapi untuk skrip rutin, ini lebih bersih dan kuat. Peringatan: periksa apakah pengguna 'postgres' tidak dapat tidak menggunakan kata sandi.
leonbloy
Ya, ada masalah dengan nama pengguna yang dibutuhkan. OTOH: Anda tidak ingin menggunakan peran lain tanpa izin koneksi.
wildplasser
3

Untuk kelengkapan, versi lain menggunakan regex daripada memotong string:

psql -l | grep '^ exact_dbname\b'

Jadi misalnya:

if psql -l | grep '^ mydatabase\b' > /dev/null ; then
  echo "Database exists already."
  exit
fi
Steve Bennett
sumber
Menggunakan \bmemiliki masalah yang sama dengan semua jawaban yang menggunakan grep -wyaitu nama-nama database dapat berisi karakter penyusun non-kata seperti -dan karena itu upaya untuk mencocokkan foojuga akan cocok foo-bar.
phils
2

jawaban yang diterima kibibu cacat karena grep -wakan cocok dengan nama apa pun yang mengandung pola yang ditentukan sebagai komponen kata.

yaitu Jika Anda mencari "foo" maka "foo-backup" cocok.

Jawaban Otheus memberikan beberapa peningkatan yang baik, dan versi pendek akan bekerja dengan benar untuk sebagian besar kasus, tetapi semakin lama dari dua varian yang ditawarkan menunjukkan masalah yang sama dengan substring yang cocok.

Untuk mengatasi masalah ini, kita bisa menggunakan -xargumen POSIX untuk mencocokkan keseluruhan saja baris teks.

Berdasarkan jawaban Otheus, versi yang baru terlihat seperti ini:

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

Itu semua mengatakan, saya cenderung mengatakan bahwa jawaban Nicolas Grilly - di mana Anda benar-benar bertanya kepada postgres tentang database spesifik - adalah pendekatan terbaik dari semua.

phils
sumber
2

Solusi lain (yang fantastis) melewatkan fakta bahwa psql dapat menunggu satu menit atau lebih sebelum waktu habis jika tidak dapat terhubung ke host. Jadi, saya suka solusi ini, yang menetapkan batas waktu menjadi 3 detik:

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

Hal ini untuk menghubungkan ke database pengembangan resmi postgres Alpine Docker gambar.

Secara terpisah, jika Anda menggunakan Rails dan ingin mengatur database jika belum ada (seperti ketika meluncurkan wadah Docker), ini berfungsi dengan baik, karena migrasi idempoten:

bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup
Dan Kohn
sumber
1

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

versi lebih pendek

Justin
sumber
0

Saya masih cukup berpengalaman dengan pemrograman shell, jadi jika ini benar-benar salah karena alasan tertentu, pilih saya, tetapi jangan terlalu khawatir.

Membangun dari jawaban 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
sumber