ฉันจะตรวจสอบไวยากรณ์ของ Bash script โดยไม่เรียกใช้ได้อย่างไร


267

เป็นไปได้หรือไม่ที่จะตรวจสอบไวยากรณ์ของ bash script โดยไม่ต้องดำเนินการ?

ใช้ Perl, perl -c 'script name'ฉันสามารถเรียกใช้ มีคำสั่งใด ๆ ที่เทียบเท่าสำหรับสคริปต์ทุบตีหรือไม่?


คำตอบ:


380
bash -n scriptname

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


9
ใน manpage ของ bash ภายใต้ "SHELL BUILTIN COMMANDS / set", -n ถูกบันทึกไว้และในตอนต้นของสถานะ manpage นั้น bash ตีความตัวเลือกอักขระเดียวทั้งหมดที่setทำ
ephemient

24
เพื่อเพิ่มไปยัง (สำหรับฉัน) ข้อแม้ที่ไม่ชัดเจนก็ยังจะไม่ได้รับข้อผิดพลาดที่เกิดจากพื้นที่ที่ขาดหายไปif ["$var" == "string" ]แทนif [ "$var" == "string" ]
Brynjar

11
@Brynjar นั่นเป็นเพราะมันเป็นเพียงการตรวจสอบไวยากรณ์ เครื่องหมายวงเล็บเปิดไม่ใช่ไวยากรณ์นั่นคือชื่อของฟังก์ชันที่จะเรียกใช้ type [พูดว่า "[เป็นเชลล์ในตัว" ท้ายที่สุดจะมอบสิทธิ์ให้กับtestโปรแกรม แต่คาดว่าจะมีวงเล็บเหลี่ยมปิดเช่นกัน ดังนั้นมันก็เหมือนif test"$var"ซึ่งไม่ใช่สิ่งที่ผู้เขียนหมายถึง แต่ถูกต้องตามหลักไวยากรณ์ (พูดว่า $ var มีค่าเป็น "a" จากนั้นเราจะเห็น "bash: testa: command not found") ประเด็นก็คือว่าวากยสัมพันธ์ไม่มีที่ขาดหายไป
Joshua Cheek

2
@JoshuaCheek: Builtin [จะเรียกเฉพาะในกรณีนี้หาก$varเกิดขึ้นจะขยายไปยังสตริงที่ว่างเปล่า หาก$varขยายไปยังสตริงที่ไม่ว่าง[จะถูกต่อกับสตริงนั้นและถูกตีความเป็นชื่อคำสั่ง (ไม่ใช่ชื่อฟังก์ชัน ) โดย Bash และใช่ว่าจะถูกต้องตามหลักไวยากรณ์แต่ตามที่คุณระบุไว้ชัดเจนว่าไม่ใช่เจตนา หากคุณใช้[[แทน[แม้ว่า[[จะเป็นคีย์เวิร์ดเชลล์(แทนที่จะเป็นบิวด์อิน) คุณจะได้รับผลลัพธ์เดียวกันเนื่องจากการต่อสตริงที่ไม่ได้ตั้งใจยังคงแทนที่คำสำคัญ
mklement0

2
@JoshuaCheek: Bash ยังคงเป็นไวยากรณ์ - ตรวจสอบที่นี่: กำลังตรวจสอบไวยากรณ์การเรียกใช้คำสั่งแบบง่าย : ["$var"เป็น ทางไวยากรณ์ของนิพจน์ชื่อคำสั่งที่ถูกต้อง ในทำนองเดียวกันราชสกุล==และ"$string"มีคำสั่งที่ถูกต้องข้อโต้แย้ง (โดยทั่วไปในตัว[จะแยกกับคำสั่งไวยากรณ์ในขณะที่[[- เป็นเปลือกคำหลัก - จะแยกแตกต่างกัน.) The builtin เปลือก[ไม่ได้ มอบหมายไป " testโปรแกรม" (ยูทิลิตี้ภายนอก): bash, dash, ksh, zshทุกคนมีในตัวรุ่นของทั้งสอง[และtestและจะไม่เรียกใช้งานยูทิลิตีภายนอก
mklement0

127

เวลาเปลี่ยนแปลงทุกอย่าง นี่คือเว็บไซต์ที่ให้บริการตรวจสอบไวยากรณ์ออนไลน์สำหรับเชลล์สคริปต์

ฉันพบว่ามันมีประสิทธิภาพมากในการตรวจจับข้อผิดพลาดทั่วไป

ป้อนคำอธิบายรูปภาพที่นี่

เกี่ยวกับ ShellCheck

ShellCheckเป็นการวิเคราะห์แบบคงที่และเป็นเครื่องมือในการทับหลังสำหรับสคริปต์ sh / bash มันมุ่งเน้นไปที่การจัดการข้อผิดพลาดทางไวยากรณ์ระดับเริ่มต้นและระดับกลางทั่วไปและข้อผิดพลาดที่เปลือกเพิ่งให้ข้อความแสดงข้อผิดพลาดที่คลุมเครือหรือพฤติกรรมแปลก ๆ แต่ก็ยังรายงานเกี่ยวกับปัญหาขั้นสูงเพิ่มเติมที่กรณีมุมอาจทำให้เกิดความล้มเหลวล่าช้า

ซอร์สโค้ดของ Haskell มีให้ที่ GitHub!


5
สุดยอดเคล็ดลับ; ใน OSX คุณสามารถตอนนี้ยังติดตั้ง CLI shellcheck.net, shellcheckผ่านHomebrewbrew install shellcheck :
mklement0

3
รวมถึงเดเบียนและเพื่อน ๆ :apt-get install shellcheck
ผู้ชายคนอื่น

สำหรับ Ubuntu trusty-backportsเชื่อถือแพคเกจนี้จะต้องมีการติดตั้งจาก
Peterino

ดังกล่าวข้างต้นเชื่อถือพึ่งพาจำเป็นและสามารถติดตั้งได้ดังต่อไปนี้ในอูบุนตู 14.04: sudo apt-get -f ติดตั้งแล้ว: sudo sudo apt apt-get ติดตั้ง shellcheck
zhihong

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

38

ฉันยังเปิดใช้งานตัวเลือก 'u' ในสคริปต์ทุบตีทุกครั้งที่ฉันเขียนเพื่อทำการตรวจสอบพิเศษ:

set -u 

สิ่งนี้จะรายงานการใช้งานตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นเช่นในสคริปต์ 'check_init.sh' ต่อไปนี้

#!/bin/sh
set -u
message=hello
echo $mesage

เรียกใช้สคริปต์:

$ check_init.sh

จะรายงานสิ่งต่อไปนี้:

./check_init.shuty4]: mesage: ไม่ได้ตั้งค่าพารามิเตอร์

มีประโยชน์มากในการจับความผิดพลาด


4
ฉันตั้งค่าสถานะเหล่านี้ในสคริปต์ทุบตีของฉันเสมอหากผ่านสิ่งเหล่านี้คุณควรไปที่ "set -o errexit" "set -o nounset" "set -o pipefail"
μολὼν.λαβέ

+1 สำหรับset -uแม้ว่าสิ่งนี้จะไม่ตอบคำถามจริงๆเพราะคุณต้องเรียกใช้สคริปต์เพื่อรับข้อความแสดงข้อผิดพลาด ไม่ได้bash -n check_init.shแสดงคำเตือนนั้น
rubo77

23
sh  -n   script-name 

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

มันทำงานได้ดีสำหรับฉัน ฉันวิ่งบน Linux OS, Bash Shell


1
แม้ว่าจะไม่เกี่ยวข้องกับการตรวจสอบไวยากรณ์ bash อย่างแน่นอน - การใช้ set -x และ set + x สำหรับการดีบักสคริปต์แบบเต็มหรือส่วนต่างๆของสคริปต์นั้นมีประโยชน์มาก
GuruM

ขอบคุณสำหรับสิ่งนี้ที่ไม่รู้ว่าไม่รู้เกี่ยวกับ -n แต่สิ่งที่ฉันต้องการคือ @GuruM> sh -x test.sh เนื่องจากแสดงผลลัพธ์ที่สร้างขึ้นจากสคริปต์
zzapper

1
ใช่. ฉันลืมที่จะพูดถึงว่าคุณสามารถทำต่อไปนี้ที่บรรทัดคำสั่ง: 1) bash -x test.sh # นี้จะเรียกใช้สคริปต์ทั้งหมดใน 'โหมดการแก้ปัญหา' 2) ตั้ง + x; ทุบตี test.sh; set -x #set โหมด debug เปิด / ปิดก่อน / หลังสคริปต์ทำงานในสคริปต์: a) #! / bin / bash -x #add 'โหมด debug' ที่ด้านบนของสคริปต์ b) set + x; รหัส; ตั้งค่า -x #add 'โหมดแก้ไขข้อบกพร่อง' สำหรับส่วนใด ๆ ของสคริปต์
GuruM

sh -nอาจจะไม่ตรวจสอบว่าสคริปต์นั้นเป็นสคริปต์ Bash ที่ถูกต้อง มันอาจให้ผลเชิงลบที่ผิดพลาด shเป็นตัวแปรเชลล์ Bourne บางตัวที่โดยปกติจะไม่ใช่ Bash ตัวอย่างเช่นใน Ubuntu Linux realpath -e $(command -v sh)ให้ / bin / dash
jarno

4

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

ตัวอย่าง:

find . -name '*.sh' -exec bash -n {} \;

หากคุณต้องการใช้เป็นไฟล์เดียวให้แก้ไขสัญลักษณ์แทนด้วยชื่อไฟล์


3

คำสั่ง null [โคลอน] มีประโยชน์เมื่อทำการดีบั๊กเพื่อดูค่าของตัวแปร

set -x
for i in {1..10}; do
    let i=i+1
    : i=$i
done
set - 

มันใช้การขยายพารามิเตอร์
mug896

งานนี้เพราะset -xแสดงทุกบรรทัดก่อนที่จะถูกดำเนินการ
rubo77

1

มีปลั๊กอิน BashSupportสำหรับIntelliJ IDEAซึ่งตรวจสอบไวยากรณ์


แต่มันจะไม่ทำงานหากไฟล์ของคุณไม่ได้ลงท้ายด้วย.shหรือส่วนขยายอื่น ๆ ที่เกี่ยวข้องกับ Bash สคริปต์ซึ่งเป็นกรณีถ้าคุณสร้างสคริปต์โดยใช้เครื่องมือสร้างเทมเพลตเช่น ERB (จากนั้นจะจบด้วย.erb) โปรดลงคะแนนให้youtrack.jetbrains.com/issue/IDEA-79574หากคุณต้องการแก้ไข!
เกร็ก Dubicki

1

หากคุณต้องการตัวแปรในการตรวจสอบความถูกต้องของไฟล์ทั้งหมดในไดเรกทอรี (git pre-commit hook, สคริปต์สร้าง lint) คุณสามารถตรวจจับเอาต์พุต stderr ของคำสั่ง "sh -n" หรือ "bash -n" (ดูอื่น ๆ คำตอบ) ในตัวแปรและมี "if / else" ตามนั้น

bashErrLines=$(find bin/ -type f -name '*.sh' -exec sh -n {} \;  2>&1 > /dev/null)
  if [ "$bashErrLines" != "" ]; then 
   # at least one sh file in the bin dir has a syntax error
   echo $bashErrLines; 
   exit; 
  fi

เปลี่ยน "sh" ด้วย "bash" ขึ้นอยู่กับความต้องการของคุณ

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