ฉันมีเชลล์สคริปต์ที่รันคำสั่งจำนวนหนึ่ง ฉันจะทำให้เชลล์สคริปต์จบการทำงานได้อย่างไรหากคำสั่งใด ๆ ออกด้วยรหัสการออกที่ไม่ใช่ศูนย์
$?
after ทุกคำสั่ง วิธีการง่าย ๆ : ใส่set -e
หรือ#!/bin/bash -e
ที่ด้านบนของสคริปต์ Bash ของคุณ
ฉันมีเชลล์สคริปต์ที่รันคำสั่งจำนวนหนึ่ง ฉันจะทำให้เชลล์สคริปต์จบการทำงานได้อย่างไรหากคำสั่งใด ๆ ออกด้วยรหัสการออกที่ไม่ใช่ศูนย์
$?
after ทุกคำสั่ง วิธีการง่าย ๆ : ใส่set -e
หรือ#!/bin/bash -e
ที่ด้านบนของสคริปต์ Bash ของคุณ
คำตอบ:
หลังจากแต่ละคำสั่งรหัสทางออกสามารถพบได้ใน$?
ตัวแปรดังนั้นคุณจะมีลักษณะดังนี้:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
คุณต้องระวังคำสั่ง piped เพราะ$?
เพียงแค่ให้รหัสส่งคืนขององค์ประกอบสุดท้ายใน pipe ดังนั้นในรหัส:
ls -al file.ext | sed 's/^/xx: /"
จะไม่ส่งคืนรหัสข้อผิดพลาดหากไฟล์นั้นไม่มีอยู่ (เนื่องจากsed
ส่วนหนึ่งของไปป์ไลน์ใช้งานได้จริงส่งคืน 0)
เปลือกจริงให้อาร์เรย์ที่สามารถให้ความช่วยเหลือในกรณีที่ผู้ที่เป็นอยู่bash
PIPESTATUS
อาร์เรย์นี้มีองค์ประกอบหนึ่งรายการสำหรับแต่ละส่วนประกอบของไปป์ไลน์ซึ่งคุณสามารถเข้าถึงทีละรายการเช่น${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]}
1
โปรดทราบว่านี่จะทำให้คุณได้ผลลัพธ์ของfalse
คำสั่งไม่ใช่ไปป์ไลน์ทั้งหมด คุณยังสามารถทำให้รายการทั้งหมดดำเนินการตามที่เห็นสมควร:
pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1
หากคุณต้องการได้รับรหัสข้อผิดพลาดที่ใหญ่ที่สุดจากไปป์ไลน์คุณสามารถใช้สิ่งต่อไปนี้:
true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc
สิ่งนี้จะผ่านแต่ละPIPESTATUS
องค์ประกอบในทางกลับกันเก็บไว้ในrc
ถ้ามันมากกว่าrc
ค่าก่อนหน้า
ls -al file.ext || exit $?
([[]] ไม่สามารถพกพาได้)
[[ ]]
เป็นอุปกรณ์พกพาที่สวยbash
ซึ่งเป็นคำถามที่ติดแท็ก :-) แปลกมากพอls
ไม่ทำงานcommand.com
ดังนั้นมันจึงไม่พกพาได้เช่นกันฉันรู้ดี แต่มันก็เป็นข้อโต้แย้งแบบเดียวกับคุณ นำเสนอ.
PIPESTATUS
${PIPESTATUS[0]}
${PIPESTATUS[1]}
${PIPESTATUS[*]}
$?
โดยตรง คุณมักต้องการบางสิ่งif ls -al file.ext; then : nothing; else exit $?; fi
ที่แน่นอนเช่น @MarcH พูดว่าเทียบเท่าls -al file.ext || exit $?
แต่ถ้าคำสั่งthen
หรือelse
ประโยคค่อนข้างซับซ้อนกว่า
[[ $rc != 0 ]]
จะทำให้คุณมีข้อผิดพลาด0: not found
หรือ นี้ควรจะเปลี่ยนไป1: not found
[ $rc -ne 0 ]
นอกจากนี้ก็จะถูกลบออกและใช้เพียงrc=$?
[ $? -ne 0 ]
หากคุณต้องการทำงานกับ $? คุณจะต้องตรวจสอบหลังจากแต่ละคำสั่งตั้งแต่ $? ถูกอัพเดตหลังจากออกแต่ละคำสั่ง ซึ่งหมายความว่าหากคุณดำเนินการไปป์ไลน์คุณจะได้รับรหัสออกจากกระบวนการสุดท้ายในขั้นตอนเท่านั้น
อีกวิธีคือ:
set -e
set -o pipefail
หากคุณวางสิ่งนี้ไว้ที่ด้านบนสุดของเชลล์สคริปต์ดูเหมือนว่าทุบตีจะดูแลสิ่งนี้ให้คุณ ดังที่ผู้โพสต์ก่อนหน้าระบุไว้ "set -e" จะทำให้ bash ออกจากโดยมีข้อผิดพลาดในคำสั่งง่ายๆ "set -o pipefail" จะทำให้ bash ออกโดยมีข้อผิดพลาดในคำสั่งใด ๆ ในไปป์ไลน์เช่นกัน
ดูที่นี่หรือที่นี่สำหรับการสนทนาเพิ่มเติมเล็กน้อยเกี่ยวกับปัญหานี้ นี่คือส่วนทุบตีด้วยตนเองในชุด builtin
PIPESTATUS
และตรวจสอบรหัสออกทุกที่
#!/bin/bash -e
เป็นวิธีเดียวที่จะเริ่มเชลล์สคริปต์ คุณสามารถใช้สิ่งต่าง ๆ เช่นfoo || handle_error $?
ถ้าคุณต้องการตรวจสอบสถานะการออกจริง
" set -e
" น่าจะเป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้ เพียงแค่ใส่คำสั่งก่อนหน้าในโปรแกรมของคุณ
set -e
สคริปต์ของคุณจะยกเลิกหากคำสั่งใด ๆ ในการออกสคริปต์ของคุณพร้อมสถานะข้อผิดพลาดและคุณไม่ได้จัดการข้อผิดพลาดนี้
set -e
เทียบเท่ากับ 100% set -o errexit
ซึ่งไม่เหมือนกับการค้นหาแบบเดิม ค้นหา opengroup + errexit เพื่อรับเอกสารอย่างเป็นทางการ
ถ้าคุณเพิ่งเรียก exit ใน bash โดยไม่มีพารามิเตอร์มันจะคืนค่า exit code ของคำสั่งสุดท้าย รวมกับหรือทุบตีควรเรียกออกเท่านั้นถ้าคำสั่งก่อนหน้านี้ล้มเหลว แต่ฉันไม่ได้ทดสอบสิ่งนี้
command1 || ออกจาก; command2 || ออกจาก;
Bash จะเก็บรหัสทางออกของคำสั่งสุดท้ายในตัวแปร $?
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
ฉันจะรับรหัสทางออกของได้อย่างไร cmd1
ในcmd1|cmd2
ก่อนอื่นโปรดทราบว่าcmd1
รหัสทางออกอาจไม่ใช่ศูนย์และยังไม่ได้หมายความว่ามีข้อผิดพลาด สิ่งนี้เกิดขึ้นเช่นใน
cmd | head -1
คุณอาจสังเกตเห็นสถานะการออก 141 (หรือ 269 ที่มี ksh93) cmd1
แต่เป็นเพราะcmd
สัญญาณ SIGPIPE ถูกขัดจังหวะเมื่อ
head -1
ยกเลิกหลังจากอ่านหนึ่งบรรทัด
หากต้องการทราบสถานะทางออกขององค์ประกอบของไปป์ไลน์
cmd1 | cmd2 | cmd3
ด้วย zsh:
รหัสทางออกมีอยู่ในอาร์เรย์พิเศษ pipestatus
cmd1
รหัสการออกอยู่ใน$pipestatus[1]
, cmd3
รหัสการออก
$pipestatus[3]
เพื่อให้$?
เป็นเช่นเดียวกับ
$pipestatus[-1]
เสมอ
ข ด้วยทุบตี:
รหัสทางออกมีอยู่ในPIPESTATUS
อาร์เรย์พิเศษ
cmd1
รหัสการออกอยู่ใน${PIPESTATUS[0]}
, cmd3
รหัสการออก
${PIPESTATUS[2]}
เพื่อให้$?
เป็นเช่นเดียวกับเสมอ
${PIPESTATUS: -1}
เสมอ
...
สำหรับทุบตี:
# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;
#
# ... the rest of the script goes here
#
function catch_errors() {
# do whatever on errors
#
#
echo "script aborted, because of errors";
exit 0;
}
ในการทุบตีสิ่งนี้เป็นเรื่องง่ายเพียงผูกเข้าด้วยกันด้วย &&:
command1 && command2 && command3
คุณยังสามารถใช้การซ้อนถ้าสร้าง:
if command1
then
if command2
then
do_something
else
exit
fi
else
exit
fi
if (! command)
หากคุณต้องการรหัสข้อผิดพลาดที่ไม่ใช่ศูนย์
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@" ;
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?
# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"
if [ $exit_code -ne 0 ] ; then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code" "$error_msg"
else
#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"
fi
}
#eof func doRunCmdOrExit
$*
; ใช้"$@"
แทนเพื่อรักษาช่องว่างและสัญลักษณ์แทน