ตรวจสอบว่ามีฐานข้อมูลใน PostgreSQL โดยใช้เชลล์หรือไม่


130

ฉันสงสัยว่าจะมีใครบอกฉันได้บ้างว่าสามารถใช้เชลล์เพื่อตรวจสอบฐานข้อมูล 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 หากไม่มี DB และ 1 ถ้าเป็นเช่นนั้น หรือ... | grep 1พฤติกรรมตรงกันข้าม
acjay

2
@ acjohnson55 ดียิ่งขึ้น: ทิ้งwcทั้งหมด ดูการแก้ไขของฉัน (ถ้าคุณต้องการที่จะกลับออกจากสถานะการทุบตีสนับสนุนผู้ประกอบการปัง: ! psql ...)
Benesch

ตอนนี้เห็นสิ่งนี้ดี
vol7ron

1
นอกเหนือจากคำแนะนำอื่น ๆ ให้วางwcคำสั่งฉันจะใช้grep -qw <term>. สิ่งนี้จะทำให้เชลล์กลับมา0หากมีการจับคู่หรือ1อย่างอื่น จากนั้น$?จะมีมูลค่าส่งคืนและคุณสามารถใช้เพื่อตัดสินใจว่าจะทำอะไรต่อไป ดังนั้นฉันไม่แนะนำให้ใช้wcในกรณีนี้ grepจะทำในสิ่งที่คุณต้องการ
Matt Friedman

ฉันได้ปรับปรุงคำตอบนี้ตามความคิดเห็นของคุณ ขอบคุณทุกคน
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
ฉันชอบที่คุณไม่ถ่ายทอดบน grep wc และสิ่งของภายนอกใด ๆ .. คุณตรวจสอบการมีอยู่ของฐานข้อมูลซึ่งหมายความว่าคุณมี psql เป็นอย่างน้อยมดนั่นเป็นคำสั่งที่น้อยที่สุดและเพียงคำสั่งเดียวที่คุณใช้! ดีมาก ๆ นอกจากหัวข้อนี้ไม่ได้กล่าวถึงประเภทเชลล์หรือเวอร์ชันของคำสั่งหรือ distro แล้ว .. ฉันไม่เคยถ่ายทอดท่อจำนวนมากไปยังเครื่องมือระบบที่ฉันเคยเห็นในคำตอบอื่น ๆ ที่รู้ว่า นำไปสู่ปัญหาหลายปีต่อมา
Riccardo Manfrin

1
ฉันเห็นด้วยกับ @RiccardoManfrin สิ่งนี้ดูเหมือนจะเป็นทางออกที่ตรงกว่า
Travis

หากคุณจำเป็นต้องดำเนินการนี้กับผู้ใช้ postgres ไม่ใช่คุณสามารถเพิ่มผู้ใช้ -U แต่ต้องแสดงรายการฐานข้อมูลในการเชื่อมต่อไปยังไม่มีใครสามารถอยู่คุณสามารถใช้ postgres template1 ฐานข้อมูลที่อยู่เสมอ: psql -U user -tAc "SELECT 1 FROM pg_database WHERE datname='DB_NAME'" template1
มกราคม

ใน psql Cygwin เพิ่มการควบคุมตัวอักษรแปลกที่จะส่งออก ( '1 \ C-M) และเป็นหนึ่งในความต้องการที่จะตรวจสอบว่าการส่งออกเพียงเริ่มต้นด้วย 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
Marius Gedminas

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"
Mike Lyons

2
การตัดทั้งหมดคืออะไร? หากคุณต้องการตรวจสอบให้แน่ใจว่าคุณกำลังดูคอลัมน์แรกเพียงแค่ใส่ไว้ใน regex: psql -l | grep '^ exact_dbname\b'ซึ่งจะตั้งค่ารหัสออกหากไม่พบ
Steve Bennett

21

ฉันเพิ่งเริ่มใช้ postgresql แต่คำสั่งต่อไปนี้คือสิ่งที่ฉันใช้เพื่อตรวจสอบว่ามีฐานข้อมูลอยู่หรือไม่

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

9
สามารถทำให้ง่ายขึ้นไปpsql ${DB_NAME} -c ''อีก
Pedro Romano

2
ดูดีสำหรับฉันแม้ว่ามันอาจจะเป็นลบที่ผิดพลาดหากมีฐานข้อมูลอยู่ แต่คุณไม่สามารถเชื่อมต่อกับมันได้ (อาจจะเป็นเช่นนั้น)
Steve Bennett

7
@SteveBennett ถ้าคุณไม่มีสิทธิ์ใด ๆ กับ DB ที่ต้องการก็จะไม่มีให้คุณ :)
Viacheslav Dobromyslov

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 เพื่อป้องกันการจับคู่ชื่อบางส่วน


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

เพื่อความสมบูรณ์เวอร์ชันอื่นที่ใช้ regex แทนการตัดสตริง:

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" จะตรงกัน

คำตอบของ Otheusมีการปรับปรุงที่ดีและเวอร์ชันสั้น ๆ จะทำงานได้อย่างถูกต้องสำหรับกรณีส่วนใหญ่ แต่ตัวแปรทั้งสองที่มีความยาวกว่าจะแสดงปัญหาที่คล้ายกันกับสตริงย่อยที่ตรงกัน

ในการแก้ไขปัญหานี้เราสามารถใช้-xอาร์กิวเมนต์POSIX เพื่อจับคู่ข้อความทั้งบรรทัดเท่านั้น

จากคำตอบของ Otheus เวอร์ชันใหม่จะมีลักษณะดังนี้:

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

ทั้งหมดที่กล่าวมาฉันมีแนวโน้มที่จะบอกว่าคำตอบของ Nicolas Grillyซึ่งเป็นที่ที่คุณถาม postgres เกี่ยวกับฐานข้อมูลเฉพาะนั้นเป็นแนวทางที่ดีที่สุด


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


0

ฉันยังไม่ค่อยมีประสบการณ์กับการเขียนโปรแกรมเชลล์ดังนั้นหากสิ่งนี้ผิดพลาดจริงๆด้วยเหตุผลบางประการให้โหวตฉันลง แต่อย่าตื่นตระหนกเกินไป

สร้างจากคำตอบของ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.