ฉันจะทำให้สคริปต์นี้มีข้อผิดพลาดในการออกจากผลของลูปได้อย่างไร


13

ฉันมีสคริปต์ทุบตีซึ่งใช้set -o errexitเพื่อให้เกิดข้อผิดพลาดสคริปต์ทั้งหมดออกจากที่จุดของความล้มเหลว
สคริปต์รันcurlคำสั่งซึ่งบางครั้งล้มเหลวในการดึงไฟล์ที่ต้องการ - อย่างไรก็ตามเมื่อเกิดเหตุการณ์นี้สคริปต์จะไม่เกิดข้อผิดพลาดในการออก

ฉันได้เพิ่มการforวนซ้ำไป

  1. หยุดชั่วครู่หนึ่งแล้วลองcurlคำสั่งอีกครั้ง
  2. ใช้falseที่ด้านล่างของ for for loop เพื่อกำหนดสถานะการออกไม่เป็นศูนย์เริ่มต้น - หากคำสั่ง curl สำเร็จ - การแบ่งลูปและสถานะการออกของคำสั่งสุดท้ายควรเป็นศูนย์
#! /bin/bash

set -o errexit

# ...

for (( i=1; i<5; i++ ))
do
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    if [ -f ~/.vim/autoload/pathogen.vim ]
    then
        echo "file has been retrieved by curl, so breaking now..."
        break;
    fi

    echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
    sleep 5
    # exit with non-zero status so main script will errexit
    false

done

# rest of script .....

ปัญหาคือเมื่อcurlคำสั่งล้มเหลวห่วง retries คำสั่งห้าครั้ง - ถ้าความพยายามทั้งหมดไม่ประสบความสำเร็จในการเสร็จสิ้นห่วงและสคริปต์หลักดำเนินการต่อ - errexitแทนวิกฤติ
ฉันจะทำให้สคริปต์ทั้งหมดจบการทำงานได้อย่างไรหากcurlคำสั่งนี้ล้มเหลว

คำตอบ:


18

แทนที่:

done

ด้วย:

done || exit 1

สิ่งนี้จะทำให้รหัสออกหากforลูปออกด้วยรหัสออกที่ไม่ใช่ศูนย์

ในฐานะที่เป็นจุดเล็กน้อยที่1ในexit 1ไม่จำเป็นต้อง exitคำสั่งล้วนจะออกด้วยสถานะออกของคำสั่งดำเนินการครั้งสุดท้ายซึ่งจะเป็นfalse(รหัส = 1) หากการดาวน์โหลดล้มเหลว หากการดาวน์โหลดสำเร็จรหัสออกจากลูปคือรหัสออกของechoคำสั่ง echoโดยปกติแล้วจะออกด้วยรหัส = 0 ซึ่งประสบความสำเร็จในเชิงสัญญาณ ในกรณีนั้นคำสั่งไม่ทำงาน||และexitคำสั่งจะไม่ถูกเรียกใช้งาน

สุดท้ายทราบว่าset -o errexitสามารถเต็มไปด้วยความประหลาดใจ สำหรับการอภิปรายของข้อดีและข้อเสียของมันให้ดูของเกร็กคำถามที่พบบ่อย #

เอกสาร

จากman bash:

สำหรับ ((expr1; expr2; expr3)); ทำรายการ เสร็จสิ้น
ขั้นแรกนิพจน์ทางคณิตศาสตร์ expr1 จะถูกประเมินตามกฎที่อธิบายไว้ด้านล่างภายใต้การประเมิน ARITHMETIC นิพจน์ทางคณิตศาสตร์ expr2 จะถูกประเมินซ้ำ ๆ จนกระทั่งมันประเมินค่าเป็นศูนย์ แต่ละครั้งที่ expr2 ประเมินค่าที่ไม่ใช่ศูนย์รายการจะถูกดำเนินการและนิพจน์ทางคณิตศาสตร์ expr3 จะถูกประเมิน หากละเว้นการแสดงออกใด ๆ ก็จะทำงานราวกับว่ามันประเมิน 1 ค่าส่งคืนคือสถานะออกจากคำสั่งสุดท้ายในรายการที่จะดำเนินการหรือเท็จถ้าการแสดงออกใด ๆ ที่ไม่ถูกต้อง [เน้นเพิ่ม]


คุณคิดว่าเป็นความคิดที่ดีหรือไม่ที่จะต้องใส่trueคำสั่ง break ให้ชัดเจนและทำให้แน่ใจว่าค่าการออกของลูปนั้นดีหรือไม่?
RobertL

1
ผมคิดว่าอย่างชัดเจนจะดีกว่าโดยปริยาย นั่นคือเหตุผลที่ฉันเขียนexit 1เมื่อธรรมดาexitจะได้ทำงาน แม้ว่ามันจะเป็นคำถามของสไตล์และอื่น ๆ สามารถมีความคิดเห็นของตัวเอง
John1024

1
ทำงานได้ดี! ขอบคุณ :) โดยส่วนตัวฉันจะอ่านexitเป็นทางออกธรรมดา - ที่จะยุติสคริปต์ในสิทธิของตนเอง exit 1 จะอ่านให้ฉันเป็น "สัญญาณ" ที่จะดำเนินการอื่น ๆ บางอย่าง (เช่นerrexit) - ว่ามันควรจะยุติสคริปต์ที่อยู่บนพื้นฐานของ "ผล" exit 1ของ - ดังนั้นฉันจึงไปด้วยexitแต่ขอบคุณสำหรับคำอธิบาย
the_velour_fog

1
ถ้าสคริปต์ของคุณจะออกเพราะเงื่อนไขข้อผิดพลาดคุณควรexit 1โทร ที่ไม่ส่งผลกระทบerrexitเลย มันแค่บอกโปรแกรมโทรว่ามีบางอย่างผิดปกติ คำสั่งมีคำสั่งอย่างใดอย่างหนึ่ง:false exit(1)99.9% ของคำสั่ง Unix คืนค่า 0 เมื่อสำเร็จและไม่เป็นศูนย์เมื่อเกิดข้อผิดพลาด ของคุณก็ควรเช่นกัน
RobertL

2

หากคุณได้errexitตั้งค่าแล้วfalseคำสั่งควรทำให้สคริปต์ออกทันที สิ่งเดียวกันถ้าcurlคำสั่งล้มเหลว

คุณเป็นตัวอย่างสคริปต์ตามที่เขียนไว้ควรออกหลังจากcurlคำสั่งแรกล้มเหลวในครั้งแรกที่เรียกใช้falseหากตั้งค่า errexit

เพื่อดูวิธีการทำงาน (ฉันใช้ชวเลข-eเพื่อตั้งerrexit:

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

ดังนั้นหากcurlคำสั่งดำเนินการมากกว่าหนึ่งครั้งสคริปต์นี้ยังไม่ได้errexitตั้งค่า


1
set -eลึกซึ้งยิ่งกว่านั้นอีก มันจะไม่ออกหลังจากคำสั่งล้มเหลวครั้งแรกในลูป คุณสามารถพิสูจน์ได้ด้วยตัวคุณเองโดยเรียกใช้(set -e; for (( i=1; i<5; i++ )); do echo $i; false; done || echo "FAIL"; )และสังเกตว่ารหัสนั้นทำงานfalseสี่ครั้ง สำหรับข้อมูลเพิ่มเติมเกี่ยวset -eดูของเกร็กคำถามที่พบบ่อย #
John1024

@ John1024 ขอบคุณ คนนี้กำลังลงและออก
RobertL

@ John1024 แต่ฉันเดาว่าหลักฐานยังerrexitไม่ถูกตั้งค่า โปรดใช้ตรรกะกับสคริปต์ในคำถาม เรียกใช้สิ่งนี้: (set -e; for (( i=1; i<5; i++ )); do echo $i; false; done ; echo still here ) ใช่การทดสอบค่าส่งคืนด้วยif while || &&ฯลฯ ไม่ทำให้เกิด errexit สคริปต์ดั้งเดิมไม่ได้||for for loop
RobertL

ฉันเพิ่งสังเกตเห็นว่าฉันไม่ได้แสดงset -o errexitคำสั่งในรหัสตัวอย่างของฉันเพิ่มตอนนี้ - และสำหรับฉันมันไม่ได้เกิดข้อผิดพลาดตามที่คาดไว้ ฉันต้องการให้falseเป็นคำสั่งสุดท้ายใน for for loop แล้วปิด loop ด้วยdone || exit [1]- จากนั้นก็ใช้งานได้ดี!
the_velour_fog

@ RobertL ฉันเห็นประเด็นของคุณ
John1024

1

set -o errexit อาจเป็นเรื่องยุ่งยากในลูปและ subshells เพราะคุณต้องผ่านทางออกกลับออกจากกระบวนการ

การแบ่งลูป (แม้ในการทำงานปกติ) ถือเป็นการฝึกฝนที่ไม่ดี คุณสามารถโทรหาฉันได้ที่โรงเรียนเก่าเพื่อเลือก while-loop มากกว่า for-loop สำหรับสองเงื่อนไข แต่ฉันคิดว่าการอ่านดีกว่า:

i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
    [ $i -eq 1 ] || sleep 5
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    RET=$?
    i=$((i+1))
done
exit $RET

0

หากerrexitตั้งค่าไว้และcurlคำสั่งล้มเหลวสคริปต์จะหยุดทำงานทันทีหลังจากคำสั่ง curl ที่ล้มเหลว ในคู่มือทุบตีไม่มีคำใบ้ที่set -eเพิกเฉยต่อสถานะการส่งคืนที่ล้มเหลวของคำสั่งเดียวในคำสั่งผสม นี่จะเป็นกรณีเดียวหากคำสั่งผสมถูกดำเนินการในบริบทที่set -eถูกละเว้น
https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin

ลองตัวอย่างที่ดัดแปลงเล็กน้อยซึ่งโพสต์โดย RobertL สิ่งนี้จะหยุดที่ for-iteration แรกทันทีหลังจากคำสั่ง false:

( set -e; for (( i=1; i<5; i++ )); do echo $i; false; echo "${i}. iteration done"; done ; echo "loop done" )

0

คุณสามารถเพิ่มตัวเลือก --fail ลงในคำสั่ง curl ซึ่งจะช่วยแก้ปัญหาของคุณสคริปต์จะล้มเหลวและออกจากข้อผิดพลาดหากคำสั่ง curl ล้มเหลวหากมีประโยชน์มากเมื่อใช้ curl ใน jenkins ไปป์:

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