วิธีการออกจากถ้าคำสั่งล้มเหลว?


222

ฉันเป็น noob ในการเขียนสคริปต์เชลล์ ฉันต้องการพิมพ์ข้อความและออกจากสคริปต์หากคำสั่งล้มเหลว ฉันพยายามแล้ว:

my_command && (echo 'my_command failed; exit)

แต่มันไม่ทำงาน มันยังคงดำเนินการตามคำแนะนำต่อไปนี้บรรทัดในสคริปต์ ฉันใช้ Ubuntu และทุบตี


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

คำตอบ:


406

ลอง:

my_command || { echo 'my_command failed' ; exit 1; }

การเปลี่ยนแปลงที่สี่:

  • เปลี่ยน&&เป็น||
  • ใช้{ }แทน( )
  • แนะนำ;หลังจากexitและ
  • ช่องว่างหลัง{และก่อน}

เนื่องจากคุณต้องการพิมพ์ข้อความและออกคำสั่งเฉพาะเมื่อล้มเหลว (ทางออกที่มีค่าที่ไม่ใช่ศูนย์) ที่คุณต้องการไม่ได้||&&

cmd1 && cmd2

จะทำงานcmd2เมื่อcmd1สำเร็จ (ค่าทางออก0) อยู่ที่ไหน

cmd1 || cmd2

จะทำงานcmd2เมื่อcmd1ล้มเหลว (ค่าออกไม่ใช่ศูนย์)

การใช้( )ทำให้คำสั่งที่อยู่ข้างในนั้นทำงานในsub-shellและการเรียก a exitจากนั้นทำให้คุณออกจาก sub-shell และไม่ใช่เชลล์เดิมของคุณดังนั้นการดำเนินการต่อในเชลล์เดิมของคุณ

เพื่อเอาชนะการใช้งานนี้ { }

ต้องการการเปลี่ยนแปลงสองครั้งล่าสุดโดย bash


4
ดูเหมือนจะเป็น "กลับรายการ" หากฟังก์ชัน "สำเร็จ" จะส่งคืน 0 และหาก "ล้มเหลว" ฟังก์ชันจะส่งกลับค่าที่ไม่ใช่ศูนย์ดังนั้นอาจ && คาดว่าจะประเมินเมื่อครึ่งแรกส่งคืนค่าที่ไม่ใช่ศูนย์ นั่นไม่ได้หมายความว่าคำตอบข้างต้นไม่ถูกต้อง - ไม่ถูกต้อง && และ || ในสคริปต์ทำงานตามความสำเร็จไม่ใช่ค่าส่งคืน
CashCow

7
ดูเหมือนจะย้อนกลับ แต่อ่านออกมาและทำให้รู้สึกว่า: "ทำคำสั่งนี้ (สำเร็จ)" หรือ "พิมพ์ข้อผิดพลาดนี้และออก"
simpleuser

2
ตรรกะที่อยู่เบื้องหลังคือภาษานั้นใช้การประเมินผลแบบลัดวงจร (SCE) ด้วย SCE f นิพจน์เป็นรูปแบบ "p OR q" และ p ถูกประเมินว่าเป็นจริงจากนั้นจึงไม่มีเหตุผลที่จะดู q หากนิพจน์อยู่ในรูปแบบ "p AND q" และ p ถูกหาค่าเป็นเท็จจะไม่มีเหตุผลที่จะดู q เหตุผลนี้เป็นสองเท่า: 1) มันเร็วกว่าด้วยเหตุผลที่ชัดเจนและ 2) มันหลีกเลี่ยงข้อผิดพลาดบางประเภท (เช่น: "if x! = 0 และ 10 / x> 5" จะผิดพลาดหากไม่มี SCE ) ความสามารถในการใช้งานในบรรทัดคำสั่งเช่นนี้เป็นผลข้างเคียงที่มีความสุข
user2635263

2
ฉันอยากจะแนะนำเพื่อสะท้อนถึง STDERR:{ (>&2 echo 'my_command failed') ; exit 1; }
rynop

127

คำตอบอื่น ๆ ได้ครอบคลุมคำถามโดยตรงดี set -eแต่คุณอาจจะสนใจในการใช้ ด้วยสิ่งนั้นคำสั่งใด ๆ ที่ล้มเหลว (นอกบริบทที่เฉพาะเจาะจงเช่นifการทดสอบ) จะทำให้สคริปต์ยกเลิก สำหรับบางสคริปต์มันมีประโยชน์มาก


3
น่าเสียดายที่มันอันตรายมาก - set -eมีกฎที่ยาวและลึกลับเกี่ยวกับเวลาที่ใช้งานและเมื่อไม่มี (หากใครก็ตามทำงานif yourfunction; then ...แล้วset -eจะไม่เริ่มทำงานภายในyourfunctionหรือสิ่งที่เรียกเพราะรหัสนั้นถูกพิจารณาว่าเป็น "ตรวจสอบ") ดูในส่วนของแบบฝึกหัดของ BashFAQ # 105พูดคุยเรื่องนี้และข้อผิดพลาดอื่น ๆ (บางส่วนใช้เฉพาะกับรุ่นเปลือกเฉพาะเท่านั้นดังนั้นคุณต้องแน่ใจว่าได้ทดสอบกับทุกรุ่นที่อาจใช้รันสคริปต์ของคุณ)
Charles Duffy

67

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

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi

10
สิ่งนี้สามารถถูกแทนที่ด้วยถ้า my_command ไม่จำเป็นต้องใช้คำสั่งทดสอบที่นี่
Bart Sas

5
+1 เพราะฉันคิดว่าคุณไม่ควรถูกลงโทษในการระบุรายชื่อตัวเลือกนี้ - ควรอยู่ในตาราง - แม้ว่ามันจะน่าเกลียดและในประสบการณ์ของฉันง่ายเกินไปที่จะเรียกใช้คำสั่งอื่นในระหว่างโดยไม่สังเกตเห็นลักษณะของการทดสอบ m แค่โง่)
Tony Delroy

1
@BartSas หากคำสั่งนั้นยาวควรวางไว้บนบรรทัดของมันเอง มันทำให้สคริปต์อ่านง่ายขึ้น
Michael

@Michael ไม่ใช่ถ้ามันสร้างการพึ่งพาข้ามเส้นตรงที่คุณต้องรู้ว่าเกิดอะไรขึ้นกับบรรทัด A ก่อนที่คุณจะเข้าใจบรรทัด B (หรือแย่กว่านั้นก็ต้องรู้ว่าจะเกิดอะไรขึ้นในบรรทัด B ก่อนที่คุณจะรู้ว่าปลอดภัย ใหม่หลังจากสิ้นสุดบรรทัด A) ฉันเคยเห็นหลายครั้งเกินไปที่มีคนเพิ่ม a echo "Just finished step A"โดยไม่ทราบว่าการechoเขียนทับสิ่ง$?ที่กำลังจะถูกตรวจสอบในภายหลัง
Charles Duffy

67

หากคุณต้องการพฤติกรรมนั้นสำหรับคำสั่งทั้งหมดในสคริปต์ของคุณเพียงเพิ่ม

  set -e 
  set -o pipefail

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

สิ่งนี้ไม่อนุญาตให้คุณพิมพ์ข้อความออก


8
คุณสามารถรันคำสั่งเมื่อออกโดยใช้คำสั่งtrapbash ในตัว
Gavin Smith

นี่คือคำตอบของคำถาม XY
jwg

5
อาจน่าสนใจที่จะอธิบายว่าคำสั่งทำอะไรได้อย่างอิสระมากกว่าคู่ โดยเฉพาะอย่างยิ่งเนื่องจากset -o pipefailอาจไม่ใช่พฤติกรรมที่ต้องการ ยังคงขอบคุณที่ชี้ให้เห็น!
แอนทอน Pinsard

3
set -eไม่น่าเชื่อถือสอดคล้องหรือคาดการณ์ได้ทั้งหมด! เพื่อโน้มน้าวตัวเองจากการที่ตรวจสอบส่วนการออกกำลังกายของBashFAQ # 105หรือตารางเปรียบเทียบพฤติกรรมเปลือกหอยที่แตกต่างกันที่in-ulm.de/~mascheck/various/set-e
ชาร์ลส์ดัฟฟี่

14

ฉันแฮ็กอัพสำนวนต่อไปนี้:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

นำหน้าแต่ละคำสั่งด้วย echo ที่ให้ข้อมูลและทำตามแต่ละคำสั่งด้วย
if [ $? -ne 0 ];...บรรทัดเดียวกัน (แน่นอนคุณสามารถแก้ไขข้อความแสดงข้อผิดพลาดได้หากต้องการ)


สิ่งนี้อาจถูกกำหนดเป็นฟังก์ชั่นจึงนำมาใช้ซ้ำ
Eric Wang

1
ถ้า [$? -ne 0]; ดังนั้น ... fi 'เป็นวิธีที่ซับซ้อนในการเขียน' || '
วินไคลน์

if ! javac *.java; then ...- ทำไมต้องกังวลด้วย$?เลย?
Charles Duffy

ชาร์ลส์ - เพราะฉันเป็นนักเจาะทุบตีที่ดีที่สุด :)
สิทธิ์ Birchmeier

10

การให้บริการmy_commandได้รับการออกแบบตามมาตรฐานของแคนนอนคือคืน 0 เมื่อสำเร็จแล้ว&&ตรงข้ามกับสิ่งที่คุณต้องการ ||คุณต้องการ

โปรดทราบด้วย(ว่าไม่ได้ดูเหมือนจะถูกต้องสำหรับฉันในการทุบตี แต่ฉันไม่สามารถลองจากที่ฉันอยู่ บอกฉัน.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}

IIRC คุณต้องการเซมิโคลอนหลังจากแต่ละบรรทัดภายใน {}
Alex Howansky


5

คุณยังสามารถใช้หากคุณต้องการรักษาสถานะข้อผิดพลาดในการออกและมีไฟล์ที่สามารถอ่านได้ด้วยหนึ่งคำสั่งต่อบรรทัด:

my_command1 || exit $?
my_command2 || exit $?

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


1
ไม่มีเหตุผลที่จะเป็นexit $?; เพียงexitใช้$?เป็นค่าเริ่มต้น
Charles Duffy

@CharlesDuffy ไม่รู้ เจ๋งมากเลยมันง่ายกว่า!
alexpirine

3

trapbuiltin เปลือกช่วยให้สัญญาณจับการและเงื่อนไขที่มีประโยชน์อื่น ๆ รวมทั้งการดำเนินการคำสั่งล้มเหลว (เช่นผลตอบแทนสถานะที่ไม่ใช่ศูนย์) ดังนั้นหากคุณไม่ต้องการทดสอบสถานะการส่งคืนของทุกคำสั่งเดียวอย่างชัดเจนคุณสามารถพูดได้trap "your shell code" ERRและรหัสเชลล์จะถูกดำเนินการเมื่อใดก็ตามที่คำสั่งส่งคืนสถานะที่ไม่เป็นศูนย์ ตัวอย่างเช่น:

trap "echo script failed; exit 1" ERR

โปรดทราบว่าเช่นเดียวกับกรณีอื่น ๆ ที่จับคำสั่งที่ล้มเหลวท่อจำเป็นต้องได้รับการดูแลเป็นพิเศษ false | trueเจตจำนงดังกล่าวข้างต้นไม่ได้จับ

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