ฉันมีเชลล์สคริปต์ที่รันคำสั่งจำนวนหนึ่ง ฉันจะทำให้เชลล์สคริปต์จบการทำงานได้อย่างไรหากคำสั่งใด ๆ ออกด้วยรหัสการออกที่ไม่ใช่ศูนย์
$?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
$*; ใช้"$@"แทนเพื่อรักษาช่องว่างและสัญลักษณ์แทน