วิธีทำบางอย่างตามเงื่อนไขหากคำสั่งสำเร็จหรือล้มเหลว


283

ฉันจะทำอะไรแบบนี้ในทุบตีได้อย่างไร

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

คำตอบ:


359

วิธีทำบางอย่างตามเงื่อนไขหากคำสั่งสำเร็จหรือล้มเหลว

นั่นคือสิ่งที่ifแถลงการณ์ของ bash ทำ:

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

การเพิ่มข้อมูลจากความคิดเห็น: คุณไม่จำเป็นต้องใช้ไวยากรณ์[... ]ในกรณีนี้ ตัวเองเป็นคำสั่งมากเกือบเทียบเท่ากับ[ testมันอาจเป็นคำสั่งที่ใช้บ่อยที่สุดในการใช้ifซึ่งสามารถนำไปสู่การสันนิษฐานว่ามันเป็นส่วนหนึ่งของไวยากรณ์ของเชลล์ แต่ถ้าคุณต้องการทดสอบว่าคำสั่งสำเร็จหรือไม่ให้ใช้คำสั่งโดยตรงด้วยifดังที่แสดงด้านบน

แก้ไข: ยกคำถามที่ด้านบนเพื่อความชัดเจน (คำตอบนี้ไม่ปรากฏใกล้ด้านบนของหน้า)


11
โปรดทราบว่าเครื่องหมายอัฒภาคมีความสำคัญ
Thorbjørn Ravn Andersen

21
หรือคุณสามารถวางthenสายแยก
l0b0

36
@ Joe: if ! command ; then ... ; fiผมคิดว่าคุณหมายถึง [เป็นคำสั่งและมันไม่จำเป็นในกรณีนี้
Keith Thompson

20
@ โจ: วิธีการของฉันยังมีคุณธรรมของการถูกต้อง if [ ! command ]ไม่ได้ดำเนินการcommand; มันจะถือว่าcommandเป็นสตริงและถือว่าเป็นจริงเพราะมันมีความยาวไม่เป็นศูนย์ [เป็นคำพ้องความหมายสำหรับtestคำสั่ง
Keith Thompson

5
อุ่ย คุณถูกต้อง
Joe

133

สำหรับสิ่งเล็ก ๆ ที่คุณต้องการให้เกิดขึ้นหากคำสั่ง shell ใช้งานได้คุณสามารถใช้&&โครงสร้างได้:

rm -rf somedir && trace_output "Removed the directory"

ในทำนองเดียวกันสำหรับสิ่งเล็ก ๆ ที่คุณต้องการให้เกิดขึ้นเมื่อคำสั่งเชลล์ล้มเหลวคุณสามารถใช้||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

หรือทั้งคู่

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

อาจไม่ฉลาดนักที่จะทำสิ่งเหล่านี้มาก แต่บางครั้งพวกเขาก็สามารถทำให้การควบคุมมีความชัดเจนมากขึ้น


3
มันสั้นและเร็วกว่า (อย่างน้อยในบางกระสุน) ฉันตัวสั่นจำสคริปต์การติดตั้ง Ultrix แบบมอนสเตอร์ที่เขียนด้วยสิ่งก่อสร้างตามเงื่อนไขเหล่านี้ฉันเคยพยายามถอดรหัส ...
vonbrand

101

ตรวจสอบค่า$?ซึ่งประกอบด้วยผลลัพธ์ของการดำเนินการคำสั่ง / ฟังก์ชันล่าสุด:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
ในขณะที่ถูกต้องทางเทคนิค (และไม่รับประกันการ downvote) มันไม่ได้ใช้ifสำนวนของ Bash ฉันชอบคำตอบของ Keith Thompson
janmoesen

16
มีประโยชน์กับสำนวนนี้ - มันรักษาค่าตอบแทน โดยรวมแล้วฉันคิดว่าอันนี้มีพลังมากกว่าแม้ว่าจะละเอียดยิ่งขึ้น นอกจากนี้ยังง่ายต่อการอ่าน
รถแท็กซี่

2
"Bash's if idiom" คืออะไร?
Nowaker

3
@ Nowaker ความจริงที่ว่าจุดประสงค์เพียงอย่างเดียวของifการทำ เงื่อนไขการควบคุมการไหลใน Bash ทั้งหมดตรวจสอบ$?เบื้องหลัง นั่นคือสิ่งที่พวกเขาทำ การตรวจสอบค่าของมันอย่างชัดเจนนั้นไม่จำเป็นในกรณีส่วนใหญ่และมักจะเป็นปฏิปักษ์มือใหม่
tripleee

1
@lunakid หากคุณไม่สนใจรหัสทางออกif ! cmdก็โอเค มิฉะนั้นข้อตกลงร่วมกันคือการใช้elseข้อ คุณไม่สามารถว่างเปล่าได้thenแต่บางครั้งคุณก็เห็นthen : nothing; elseว่า:ไม่มีตัวละครสำคัญ trueจะทำงานที่นั่นเช่นกัน
tripleee

47

สิ่งนี้ใช้ได้กับฉัน:

command && echo "OK" || echo "NOK"

หากcommandประสบความสำเร็จก็echo "OK"จะถูกดำเนินการและเนื่องจากมันประสบความสำเร็จการดำเนินการหยุดที่นั่น มิฉะนั้น&&จะถูกข้ามและecho "NOK"ถูกดำเนินการ


5
หากคุณต้องการทำบางสิ่งหากล้มเหลวและเก็บรหัสทางออกไว้ (เพื่อแสดงในพร้อมท์คำสั่งหรือทดสอบในสคริปต์) คุณสามารถทำสิ่งนี้ได้: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Hasler Sam

2
@ Sam-Hasler: นั่นไม่ควรcommand && echo "OK" || (c=$?; echo "NOK"; (exit $c))เหรอ?
jrw32982

9
นอกจากนี้หากecho "OK"ส่วนใดส่วนหนึ่งอาจล้มเหลวตัวเองก็จะดีขึ้น:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982

@ jrw32982, นิสัยดีฉันเคยใช้การก่อสร้างแบบเดิม แต่ไม่ใช่แบบหลัง
Sam Hasler

คำตอบที่แท้จริงอยู่ในความคิดเห็นขอบคุณทุกคน!
Joshua Pinter

6

ควรสังเกตว่าif...then...fiและ&&/ ||ประเภทของวิธีการจัดการกับสถานะทางออกที่ส่งคืนโดยคำสั่งที่เราต้องการทดสอบ (0 เมื่อสำเร็จ); อย่างไรก็ตามคำสั่งบางคำสั่งจะไม่ส่งคืนสถานะการออกที่ไม่ใช่ศูนย์หากคำสั่งล้มเหลวหรือไม่สามารถจัดการกับอินพุตได้ ซึ่งหมายความว่าปกติifและ&&/ ||แนวทางจะไม่ทำงานสำหรับคำสั่งเฉพาะเหล่านั้น

ตัวอย่างเช่นบน Linux GNU fileยังคงออกด้วย 0 หากได้รับไฟล์ที่ไม่มีอยู่เป็นอาร์กิวเมนต์และfindไม่สามารถระบุตำแหน่งผู้ใช้ไฟล์ที่ระบุ

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

ในกรณีเช่นนี้วิธีที่เป็นไปได้วิธีหนึ่งที่เราสามารถจัดการกับสถานการณ์ได้คือการอ่านstderr/ stdinข้อความเช่นที่ส่งคืนโดยfileคำสั่งหรือแยกวิเคราะห์เอาต์พุตของคำสั่งเช่นfindนี้ สำหรับวัตถุประสงค์นั้นcaseสามารถใช้ข้อความสั่งได้

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

ข้อผิดพลาดมากที่สุดที่ฉันสามารถทำได้คือ:

  • ขั้นแรกรับค่า สมมติว่าคุณทำสิ่งที่ชอบ:

RR=$?

ตอนนี้ไม่เพียง แต่สถานการณ์นี้ แต่คนอื่น ๆ ที่คุณอาจเผชิญให้พิจารณา:

ตัวแปรที่กำหนด:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

ตอบ: ใช่

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

คำตอบ: ไม่

ตัวแปรที่ไม่ได้กำหนด:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

คำตอบ: ไม่

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

ตอบ: ใช่

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

ตอบ: ใช่

เพื่อป้องกันข้อผิดพลาดทุกชนิด

คุณอาจรับรู้ถึงไวยากรณ์ทั้งหมด แต่นี่เป็นเคล็ดลับ:

  • ใช้คำพูด หลีกเลี่ยงที่จะเป็น"blank"nothing
  • ${variable}สัญกรณ์ใหม่ที่ทันสมัยสำหรับตัวแปรคือ
  • การเพิ่มศูนย์ที่ต่อกันก่อนที่หมายเลขของคุณจะหลีกเลี่ยง "ไม่มีตัวเลขเลย"
  • base-8แต่รอการเพิ่มศูนย์ทำให้จำนวนกลายเป็น คุณจะได้รับข้อผิดพลาดเช่น:
    • value too great for base (error token is "08")7สำหรับตัวเลขดังกล่าวข้างต้น นั่นคือเมื่อ10#เข้ามาเล่น:
    • 10#base-10กองกำลังจำนวนที่จะ

1

คุณสามารถทำได้:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

9
ใช้งานได้ แต่มีความซับซ้อน คุณกำลังเรียกใช้ ping ใน subshell ของ subshell ผลลัพธ์ของpingถูกจับในมุมมองของการเรียกใช้เป็นคำสั่ง แต่เนื่องจากผลลัพธ์ถูกเปลี่ยนเส้นทางไปยัง / dev / null ที่จะเป็นสตริงว่างเสมอ ดังนั้นคุณจะไม่ทำงานอะไรใน subshell ซึ่งหมายความว่าสถานะการออกก่อนหน้า (ของ subshell การแทนที่คำสั่งที่เป็น ping) จะถูกเก็บไว้ เห็นได้ชัดว่าวิธีที่ถูกต้องอยู่if ping ...; thenที่นี่
Stéphane Chazelas เมื่อ

1

ตามที่ระบุไว้ที่อื่นในหัวข้อนี้คำถามเดิมโดยทั่วไปตอบตัวเอง นี่คือภาพประกอบที่แสดงว่าifเงื่อนไขอาจซ้อนกัน

ตัวอย่างนี้ใช้ifเพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่และเป็นไฟล์ปกติหรือไม่ หากเงื่อนไขเหล่านั้นเป็นจริงให้ตรวจสอบว่ามีขนาดใหญ่กว่า 0 หรือไม่

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
นั่นคือสิ่งที่คำตอบของคุณทำ แต่คำตอบของคุณไม่ได้ตอบคำถาม
Jeff Schaller

@JeffSchaller ขอบคุณสำหรับบันทึกย่อของคุณ ฉันแก้ไขโพสต์ของฉันเพื่อรวมการอ้างอิงถึงคำถาม
quartzinquartz

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
ดูเหมือนว่าจะเป็นคำตอบที่ยอมรับอยู่แล้ว โปรดระบุงานที่คุณนำมาใช้ใหม่หรือเพิ่มความพยายามของคุณเองลงในคำตอบของคุณ
Jeff Schaller

-3

สิ่งนี้สามารถทำได้ง่ายๆด้วยวิธีนี้เนื่องจาก$?ให้สถานะของคำสั่งสุดท้ายที่ถูกเรียกใช้งาน

ดังนั้นมันอาจจะเป็น

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

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