มีวิธีที่จะมีฟังก์ชั่นในสคริปต์อัตโนมัติทุบตีของฉันทำงานกับข้อผิดพลาดคำสั่งใด ๆ ?


12

ฉันกำลังเขียนเชลล์สคริปต์ที่ต้องทำพวงคำสั่งและทุกคำสั่งขึ้นอยู่กับทุกคำสั่งก่อนหน้า หากคำสั่งใด ๆ ล้มเหลวสคริปต์ทั้งหมดควรล้มเหลวและฉันเรียกใช้ฟังก์ชัน exit ฉันสามารถตรวจสอบรหัสออกของแต่ละคำสั่ง แต่ฉันสงสัยว่ามีโหมดที่ฉันสามารถเปิดใช้งานหรือวิธีการทุบตีให้ทำโดยอัตโนมัติ

ตัวอย่างเช่นใช้คำสั่งเหล่านี้:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


มีวิธีที่ฉันสามารถส่งสัญญาณไปยังเปลือกอย่างใดก่อนที่จะดำเนินการคำสั่งเหล่านี้ว่ามันควรจะเรียก myfunc หากพวกเขาล้มเหลวเพื่อให้ฉันสามารถเขียนสิ่งที่สะอาดเช่น:

cd foo
rm a
cd bar
rm b

คำตอบ:


13

คุณสามารถใช้ bash trap ERRเพื่อให้สคริปต์ของคุณออกจากหากคำสั่งใด ๆ ส่งคืนสถานะที่มากกว่าศูนย์และดำเนินการฟังก์ชันของคุณเมื่อออก

สิ่งที่ต้องการ:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

โปรดทราบว่ากับดักทุบตี ERRนั้นโดยปริยายset -o errexitหรือset -eและไม่ POSIX

และERRกับดักจะไม่ถูกดำเนินการหากคำสั่งที่ล้มเหลวเป็นส่วนหนึ่งของรายการคำสั่งที่ตามมาทันทีuntilหรือwhileคำหลักส่วนหนึ่งของการทดสอบทำตามคำifหรือelifคำสงวนส่วนหนึ่งของคำสั่งที่ดำเนินการใน&&หรือ||รายการหรือหากสถานะการส่งคืนคำสั่ง!.


1

คำตอบที่ยอมรับได้ง่ายขึ้น (อาจจะ)

  1. ใช้set -e เพื่อทำให้ความล้มเหลวของคำสั่งเดียวยกเลิกการดำเนินการของรายการ
  2. เพียงแค่รายการคำสั่งของคุณ
  3. ใช้if- then- elseคำสั่งให้ดำเนินการคำสั่งจัดการข้อผิดพลาดของคุณ (s) ชิ้นสุดท้ายนี้ค่อนข้างยุ่งยาก ดู:
ชุด -e
ถ้า
    cmd 1                         # เช่น cd foo
     cmd 2                         # เช่น rm a
     cmd 3                         # เช่น cd bar
     cmd 4                         # เช่น rm b
แล้วก็
    set + e
    คำสั่งให้ทำตามความสำเร็จ (ถ้ามี)
อื่น
    set + e
    MyFunc
    คำสั่งอื่น ๆ ที่ต้องทำเมื่อล้มเหลว (ถ้ามี) 
fi

ส่วนที่ยุ่งยากคือคุณใส่คำสั่ง ลงในifส่วนของif- then- elseไม่ใช่thenส่วนหรือelseส่วน จำได้ว่าไวยากรณ์ของifคำสั่งคือ

ถ้า รายการ ; รายการแล้ว  ; [ รายการelif ; รายการแล้ว ; ] ... [ รายการอื่น ; ] fi 
   ↑↑↑↑   
set -eบอกเปลือกว่าถ้า( ) ล้มเหลวก็ไม่ควรไปและดำเนินการ ( ) และอื่น ๆ ลงเส้น หากสิ่งนี้เกิดขึ้นกับคำสั่งที่ระดับนอกสุดของเชลล์สคริปต์เชลล์จะออก อย่างไรก็ตามตั้งแต่· · · เป็น (สารประกอบ) รายการดังต่อไปนี้ความล้มเหลวใด ๆ ของบรรดาสี่คำสั่งก็เป็นสาเหตุที่ทำให้รายชื่อทั้งหมดที่จะล้มเหลว - ซึ่งเป็นสาเหตุที่ทำให้ประโยคที่จะดำเนินการ หากทั้งสี่คำสั่งประสบความสำเร็จส่วนคำสั่งจะถูกดำเนินการcmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

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

set -eมีการระบุและอธิบายไว้ในข้อกำหนด POSIX


0

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

สิ่งที่คุณต้องใช้คือเชื่อมโยงคำสั่งของคุณกับ&&โอเปอเรเตอร์และ||โอเปอเรเตอร์ซึ่งทำสิ่งที่คุณเขียน

ยกตัวอย่างเช่นห่วงโซ่นี้จะยากจนและจะพิมพ์ "สิ่งที่ผิดพลาด" ถ้าใด ๆของคำสั่งก่อนหน้านี้ยากจน (ทุบตีอ่านจากซ้ายไปขวา)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

ตัวอย่างจริง (ฉันสร้าง dir foo, ไฟล์ a, บาร์ dir และไฟล์ b สำหรับการสาธิตจริง):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

และในที่สุดหากคำสั่งทั้งหมดได้รับการดำเนินการเรียบร้อยแล้ว (รหัสทางออก 0) สคริปต์จะดำเนินการต่อไป:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

สิ่งสำคัญที่ต้องจำคือเมื่อใช้คำสั่ง && ถัดไปจะถูกดำเนินการหากคำสั่งก่อนหน้าออกจากด้วยรหัส 0 ซึ่งสำหรับ bash หมายถึงความสำเร็จ

หากคำสั่งใด ๆ ผิดพลาดในห่วงโซ่แล้วคำสั่ง / สคริปต์ / สิ่งต่อไปนี้ || จะถูกประหารชีวิต

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

ตัวอย่าง:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

เอาท์พุท:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

เคล็ดลับ: คุณสามารถระงับข้อความ "bash: cd: bbar: ไม่มีไฟล์ดังกล่าว ... " โดยใช้ eval $comm 2>/dev/null

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