Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> การเขียนโปรแกรม Bash

5 เทคนิคพิสูจน์แล้ว เพื่อเพิ่มประสิทธิภาพ Bash Script ให้เชื่อถือได้และมีประสิทธิภาพ

ซิสแอดมินมักเขียนสคริปต์ Bash บางตัวสั้น บางตัวยาวมาก เพื่อทำภารกิจต่างๆ

คุณเคยดูสคริปต์ติดตั้งที่ผู้จัดจำหน่ายซอฟต์แวร์ให้มาไหม? พวกเขามักเพิ่มฟังก์ชันและตรรกะเยอะ เพื่อให้แน่ใจว่าติดตั้งได้ถูกต้องและไม่ทำลายระบบลูกค้า ตลอดปีผ่านมา ผมสะสมเทคนิคต่างๆ สำหรับปรับปรุงสคริปต์ Bash ของผม และอยากแบ่งปันบางส่วน หวังว่าจะช่วยผู้อื่น นี่คือชุดสคริปต์เล็กๆ ที่สร้างขึ้นเพื่อแสดงตัวอย่างง่ายๆ เหล่านี้

การเริ่มต้น

ตอนเริ่มต้น สคริปต์ Bash ของผมเป็นเพียงชุดคำสั่งปกติ มักใช้เพื่อประหยัดเวลากับอเปอเรชันเชลล์มาตรฐาน เช่น การปรับใช้เนื้อหาเว็บแบบคงที่ (Static Content) สคริปต์หนึ่งทำหน้าที่แยกเนื้อหาคงที่ลงในไดเรกทอรีบ้านของเว็บเซิร์ฟเวอร์ Apache สคริปต์ของผมเป็นประมาณนี้:

cp january_schedule.tar.gz /usr/apache/home/calendar/
cd /usr/apache/home/calendar/
tar zvxf january_schedule.tar.gz

แม้จะประหยัดเวลาและการพิมพ์บ้าง แต่นี่แน่นอนไม่ใช่สคริปต์ที่น่าสนใจหรือมีประโยชน์ในระยะยาว ตามเวลา ผมเรียนรู้วิธีอื่นๆ ใช้สคริปต์ Bash ทำงานที่ท้าทายกว่า เช่น สร้างแพ็กเกจซอฟต์แวร์ ติดตั้งซอฟต์แวร์ หรือสำรองข้อมูลไฟล์เซิร์ฟเวอร์

1. คำสั่งเงื่อนไข (Conditional Statement)

เช่นเดียวกับภาษาโปรแกรมมิ่งอื่นๆ หลายตัว คำสั่งเงื่อนไข (Conditional) เป็นคุณสมบัติที่ทรงพลังและทั่วไป ตัวอย่างส่วนใหญ่ของผมอิงตามตรรกะเงื่อนไข

เงื่อนไขพื้นฐานใช้คำสั่ง "if" นี่ช่วยให้เราทดสอบเงื่อนไขบางอย่าง ที่เราสามารถใช้จัดการการทำงานของสคริปต์ได้ ตัวอย่างเช่น เราสามารถตรวจสอบความมีอยู่ของไดเรกทอรี bin ของ Java ซึ่งจะบ่งบอกว่า Java ติดตั้งแล้ว หากพบ สามารถอัปเดตเส้นทางการรัน (Executable Path) ด้วยตำแหน่งนั้น เพื่อให้แอปพลิเคชัน Java เรียกใช้ได้

if [ -d "$JAVA_HOME/bin" ] ; then
PATH="$JAVA_HOME/bin:$PATH"

2. จำกัดการรัน (Limit Execution)

คุณอาจต้องการจำกัดสคริปต์ให้รันได้เฉพาะผู้ใช้เฉพาะคน แม้ Linux จะมีสิทธิ์มาตรฐานสำหรับผู้ใช้และกลุ่ม รวมถึง SELinux สำหรับการป้องกันแบบนี้ แต่คุณอาจเลือกวางตรรกะภายในสคริปต์ บางทีคุณอยากแน่ใจว่าเฉพาะเจ้าของเว็บแอปพลิเคชันเฉพาะตัวเท่านั้นที่รันสคริปต์เริ่มต้นได้ คุณแม้แต่สามารถใช้โค้ดจำกัดสคริปต์ให้เฉพาะผู้ใช้ root Linux มีตัวแปรสภาพแวดล้อม 2 ตัวที่เราสามารถทดสอบในตรรกะนี้ หนึ่งคือ $USER ซึ่งให้ชื่อผู้ใช้อีกตัวคือ $UID ซึ่งให้หมายเลขระบุตัวตนผู้ใช้ (UID) และในกรณีของสคริปต์ คือ UID ของผู้ใช้ที่รัน

ผู้ใช้ (User)

ตัวอย่างแรกแสดงวิธีที่ผมจำกัดสคริปต์ให้เฉพาะผู้ใช้ jboss1 ในสภาพแวดล้อม Multi-hosting ที่มีหลายอินสแตนซ์แอปพลิเคชันเซิร์ฟเวอร์ คำสั่งเงื่อนไข "if" ถามว่า "ผู้ใช้ที่รันไม่ใช่ jboss1 ไหม?" เมื่อเงื่อนไขเป็นจริง คำสั่ง echo แรกจะถูกเรียก ตามด้วย exit 1 ซึ่งจบสคริปต์

if [ "$USER" != 'jboss1' ]; then
echo "Sorry, this script must be run as JBOSS1!"
exit 1
fi
echo "continue script"

Root

ตัวอย่างสคริปต์ถัดไป ให้แน่ใจว่าเฉพาะผู้ใช้ root เท่านั้นที่รันได้ เนื่องจาก UID ของ root คือ 0 เราสามารถใช้ตัวเลือก -gt ในคำสั่ง if เงื่อนไข เพื่อห้าม UID ทั้งหมดที่มากกว่าศูนย์

if [ "$UID" -gt 0 ]; then
echo "Sorry, this script must be run as ROOT!"
exit 1
fi
echo "continue script"

3. ใช้ ארกิวเมนต์ (Arguments)

เหมือนโปรแกรมรันได้ทั่วไป สคริปต์ Bash สามารถรับอาร์กิวเมนต์เป็นอินพุตได้ นี่คือตัวอย่างบางส่วน แต่ก่อน คุณควรเข้าใจว่า การเขียนโปรแกรมที่ดี หมายถึงเราไม่ได้แค่เขียนแอปที่ทำสิ่งที่เราอยากทำ เราต้องเขียนแอปที่ ไม่สามารถ ทำสิ่งที่เรา ไม่อยาก ให้ทำได้ ผมชอบให้แน่ใจว่าสคริปต์ไม่ทำอะไรทำลายล้าง ในกรณีที่ไม่มีอาร์กิวเมนต์ ดังนั้น นี่คือการตรวจสอบแรก เงื่อนไขตรวจสอบจำนวนอาร์กิวเมนต์ $# ว่าเป็นศูนย์หรือไม่ และจบสคริปต์หากเป็นจริง

if [ $# -eq 0 ]; then
echo "No arguments provided"
exit 1
fi
echo "arguments found: $#"

อาร์กิวเมนต์หลายตัว (Multiple Arguments)

คุณสามารถส่งอาร์กิวเมนต์มากกว่าหนึ่งตัวให้สคริปต์ ตัวแปรภายในที่สคริปต์ใช้อ้างอิงแต่ละอาร์กิวเมนต์ เพียงแค่เพิ่มขึ้นทีละตัว เช่น $1, $2, $3 และต่อไป ผมจะขยายตัวอย่างข้างต้นด้วยบรรทัดต่อไป เพื่อ echo อาร์กิวเมนต์ 3 ตัวแรก แน่นอนว่าจะต้องมีตรรกะเพิ่มเติมสำหรับการจัดการอาร์กิวเมนต์ที่เหมาะสมตามจำนวนทั้งหมด ตัวอย่างนี้ง่ายเพื่อการสาธิต

echo $1 $2 $3

ขณะที่เราพูดถึงตัวแปรอาร์กิวเมนต์เหล่านี้ คุณอาจเคยสงสัยว่า "เขาข้ามศูนย์ไปไหม?"

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

echo $0

เหตุผลสำคัญในการอ้างอิงชื่อสคริปต์ระหว่างการรัน คือเพื่อสร้างไฟล์ Log ที่รวมชื่อสคริปต์ในชื่อของมันเอง รูปแบบง่ายที่สุดอาจแค่คำสั่ง echo

echo test >> $0.log

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

4. รับอินพุตจากผู้ใช้ (User Input)

คุณสมบัติอีกอย่างที่มีประโยชน์ในสคริปต์ คือความสามารถในการรับอินพุตระหว่างการรัน ง่ายที่สุดคือเสนอให้ผู้ใช้ป้อนอินพุตบางอย่าง

echo "enter a word please:"
read word
echo $word

นี่ยังช่วยให้คุณเสนอตัวเลือกให้ผู้ใช้ได้

read -p "Install Software ?? [Y/n]: " answ
if [ "$answ" == 'n' ]; then
exit 1
fi
echo "Installation starting..."

5. ออกเมื่อล้มเหลว (Exit on Failure)

หลายปีก่อน ผมเขียนสคริปต์สำหรับติดตั้ง Java Development Kit (JDK) รุ่นล่าสุดบนคอมพิวเตอร์ของผม สคริปต์แตกไฟล์ JDK ไปยังไดเรกทอรีเฉพาะ อัปเดตซิมบอลิกลิงก์ และใช้ยูทิลิตี้ alternatives เพื่อให้ระบบทราบรุ่นใหม่ หากการแตกไฟล์ JDK ล้มเหลว การต่อไปอาจทำลาย Java ทั่วระบบ ดังนั้น ผมอยากให้สคริปต์หยุดในสถานการณ์เช่นนั้น ผมไม่อยากให้สคริปต์ทำการเปลี่ยนแปลงระบบชุดถัดไป เว้นแต่แอดไดฟ์ถูกแตกสำเร็จ นี่คือส่วนหนึ่งจากสคริปต์นั้น:

tar kxzmf jdk-8u221-linux-x64.tar.gz -C /jdk --checkpoint=.500; ec=$?
if [ $ec -ne 0 ]; then
echo "Installation failed - exiting."
exit 1
fi

วิธีรวดเร็วสำหรับคุณสาธิตการใช้งานตัวแปร $? คือกับ one-linner สั้นนี้:

ls T; ec=$?; echo $ec

ก่อน รัน touch T ตามด้วยคำสั่งนี้ ค่าของ ec จะเป็น 0 จากนั้น ลบ T, rm T และทำคำสั่งซ้ำ ค่าของ ec จะกลายเป็น 2 เพราะ ls รายงานสภาวะข้อผิดพลาด เนื่องจาก T ไม่พบ

คุณสามารถนำการรายงานข้อผิดพลาดนี้ไปใช้ รวมถึงตรรกะ ดังที่ผมทำข้างต้น เพื่อควบคุมพฤติกรรมของสคริปต์ของคุณ

ข้อสรุป (Takeaway)

เราอาจสมมติว่าต้องใช้ภาษาเช่น Python, C, หรือ Java สำหรับฟังก์ชันระดับสูง แต่ไม่จำเป็นจริงๆ ภาษาสคริปต์ Bash ทรงพลังมาก มีอะไรให้เรียนรู้เยอะ เพื่อเพิ่มประโยชน์สูงสุด หวังว่าตัวอย่างน้อยๆ นี้จะส่องแสงให้เห็นศักยภาพของการเขียนโค้ดด้วย Bash