Bash ละเว้นข้อผิดพลาดสำหรับคำสั่งเฉพาะ


444

ฉันใช้ตัวเลือกต่อไปนี้

set -o pipefail
set -e

ในสคริปต์ทุบตีเพื่อหยุดการดำเนินการเกี่ยวกับข้อผิดพลาด ฉันมีสคริปต์ประมาณ 100 บรรทัดและฉันไม่ต้องการตรวจสอบรหัสส่งคืนของทุกบรรทัดในสคริปต์

แต่สำหรับหนึ่งคำสั่งเฉพาะฉันต้องการละเว้นข้อผิดพลาด ฉันจะทำสิ่งนั้นได้อย่างไร

คำตอบ:


757

การแก้ไขปัญหา:

particular_script || true

ตัวอย่าง:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three จะไม่ถูกพิมพ์

นอกจากนี้ฉันต้องการเพิ่มว่าเมื่อpipefailเปิดอยู่ก็เพียงพอแล้วที่เชลล์จะคิดว่าไพพ์ทั้งหมดมีโค้ดออกที่ไม่เป็นศูนย์เมื่อคำสั่งใดคำสั่งหนึ่งในไพพ์นั้นมีโค้ดทางออกที่ไม่เป็นศูนย์ (โดยpipefailจะต้องปิดตัวสุดท้าย .

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0

15
+1 ตามที่คู่มืออ้างอิง Bash อธิบายว่า "เชลล์ไม่ออก" เมื่อ-eมีการตั้งค่าแอททริบิวต์ "หากคำสั่งที่ล้มเหลวเป็นส่วนหนึ่งของรายการคำสั่งต่อจากคำสั่งwhileหรือuntilคำหลักทันทีส่วนหนึ่งของการทดสอบในifคำสั่ง ใน&&หรือ||รายการยกเว้นคำสั่งที่ตามหลังคำสั่งสุดท้าย&&หรือ||คำสั่งใด ๆ ในไปป์ไลน์ แต่เป็นคำสั่งสุดท้ายหรือหากสถานะการส่งคืนของคำสั่งกลับด้านด้วย!"
ruakh

@IgorChubin ฉันไม่รู้ว่าทำไม แต่นี่ไม่ทำงาน output = (ldd $2/bin/* || true) | grep "not found" | wc -lสคริปต์จะถูกยกเลิกหลังจากบรรทัดนี้เมื่อ ldd return failure
Vivek Goel

1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
Igor Chubin

1
set -o pipefailเหตุ เมื่อgrepพบว่าไม่มีอะไรมันจะส่งคืนโค้ดออกที่ไม่เป็นศูนย์และมันก็เพียงพอสำหรับเชลล์ที่จะคิดว่าไพพ์ทั้งหมดมีโค้ดออกที่ไม่ใช่ศูนย์
Igor Chubin

3
หากคุณต้องการตัวเลือกในการรักษาค่าส่งคืน (โดยไม่ต้องออก) คุณลองคำสั่ง && จริง สิ่งนี้ช่วยให้คุณสามารถตรวจสอบรหัสส่งคืนในขั้นตอนต่อมาและจัดการโดยทางโปรแกรม
Ed Ost

179

เพียงเพิ่ม|| trueหลังจากคำสั่งที่คุณต้องการละเว้นข้อผิดพลาด


11
ฉันคิดว่ามันเป็นสิ่งสำคัญที่จะเพิ่มนี้: วิธีนี้จะช่วยให้รหัสการตอบสนองและข้อความแสดงข้อผิดพลาดยังคงมีอยู่ในขณะที่ "!" วิธีการที่ระบุไว้ด้านล่างจะเปลี่ยนรหัสการตอบสนองและทำให้ไม่เกิดข้อผิดพลาด สิ่งนี้มีความสำคัญเมื่อใช้งานset -eและพยายามดักจับข้อผิดพลาด: เช่นset -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky

87

อย่าหยุดและยังบันทึกสถานะออก

ในกรณีที่คุณต้องการให้สคริปต์ของคุณไม่หยุดหากคำสั่งเฉพาะล้มเหลวและคุณต้องการบันทึกรหัสข้อผิดพลาดของคำสั่งที่ล้มเหลวด้วย:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE

2
นี่คือสิ่งที่ฉันต้องการ
sarink

ใช้งานไม่ได้สำหรับฉันด้วยเหตุผลบางอย่าง ... โอเค
Jeef

EXIT_CODE ไม่ได้ถูกตั้งค่าเป็นศูนย์เมื่อคำสั่งส่งคืนค่า 0 รหัสการออกที่ไม่ใช่ศูนย์จะถูกดักจับแม้ว่า มีความคิดอะไรไหม
Ankita13

@ Ankita13 เพราะถ้ามันเป็นศูนย์แรกที่ประสบความสำเร็จและมีความจำเป็นที่จะเรียกใช้สิ่งที่อยู่หลังcommand ||อ่านมันเป็นถ้าล้มเหลวทำcommand EXIT_CODE=$?คุณอาจจะทำcommand || echo "$?"หรือใช้ "กับดัก" เพื่อแก้ไขข้อบกพร่องอย่างละเอียด ดูสิ่งนี้ -> stackoverflow.com/a/6110446/10737630
tinnick

63

รัดกุมมากขึ้น:

! particular_script

จากข้อกำหนดของ POSIXเกี่ยวกับset -e(การเน้นที่เหมือง):

เมื่อเปิดใช้ตัวเลือกนี้หากคำสั่งอย่างง่ายล้มเหลวด้วยเหตุผลใดก็ตามที่ระบุไว้ในผลที่ตามมาของข้อผิดพลาดของเชลล์หรือส่งกลับค่าสถานะการออก> 0 และไม่ได้เป็นส่วนหนึ่งของรายการประกอบต่อไปนี้ ไม่ได้เป็นส่วนหนึ่งของรายการ AND หรือ OR และไม่ใช่ขั้นตอนนำหน้าด้วย! คำสงวนแล้วเปลือกจะออกทันที


16
เพียงแค่นี้ก็รหัสทางออกตีความของคำสั่งเพื่อให้คำสั่งที่เสร็จเรียบร้อยแล้วจะกลับ 1 แทน 0 -eและล้มเหลวสคริปต์ด้วย
Marboni

17
ความเข้าใจของฉันคือการที่!จะป้องกันไม่ให้เปลือกออกจากอะไร สคริปต์นี้แสดงข้อความStill alive!เมื่อฉันเรียกใช้แสดงว่าสคริปต์รันจนเสร็จ คุณเห็นพฤติกรรมที่แตกต่างหรือไม่?
Lily Finley

7
คุณถูกต้องมันจะเปลี่ยนสถานะการออก แต่จะไม่เกิดข้อผิดพลาดสคริปต์เมื่อคำสั่งลงท้ายด้วยทั้ง 0 หรือ 1 ฉันไม่ตั้งใจ อย่างไรก็ตามขอขอบคุณสำหรับการอ้างถึงเอกสารที่ฉันใส่การแสดงออกของฉันเพื่อifข้อและแก้ไขปัญหา
Marboni

42

แทนที่จะเป็น "return true" คุณสามารถใช้ยูทิลิตี noop หรือ null (ตามที่อ้างถึงในPOSIX specs ) :และเพียงแค่ "ไม่ทำอะไรเลย" คุณจะบันทึกตัวอักษรสองสามตัว :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."

3
แม้ว่า ||: จะไม่ชัดเจนเหมือน | | จริง (ซึ่งอาจทำให้ผู้ใช้ที่ไม่ใช่ผู้เชี่ยวชาญสับสน) ฉันชอบความรัดกุม
เดวิด

ชุดตัวเลือกนี้ไม่ส่งคืนข้อความใด ๆ ที่มีความสำคัญใน CI
kivagant

5

หากคุณต้องการป้องกันสคริปต์ของคุณล้มเหลวและรวบรวมรหัสส่งคืน:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode ถูกรวบรวมไม่ว่าคำสั่งจะสำเร็จหรือล้มเหลว


2

ฉันใช้ข้อมูลโค้ดด้านล่างเมื่อทำงานกับเครื่องมือ CLI และฉันต้องการทราบว่ามีทรัพยากรบางส่วนอยู่หรือไม่ แต่ฉันไม่สนใจเกี่ยวกับผลลัพธ์

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi

1
นี่ดูเหมือนจะเป็นวงเวียนที่แท้จริงและมีราคาแพงพอสมควรที่จะพูดif [ -r not_exist ]
tripleee

1

ฉันชอบวิธีนี้:

: `particular_script`

คำสั่ง / สคริปต์ระหว่างเครื่องหมายขีดหลังถูกเรียกใช้งานและเอาต์พุตจะถูกป้อนไปยังคำสั่ง ":" (ซึ่งเทียบเท่ากับ "true")

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

แก้ไข: แก้ไขตัวพิมพ์ที่น่าเกลียด


0

ในขณะที่|| trueเป็นที่ต้องการ แต่คุณสามารถทำได้

var=$(echo $(exit 1)) # it shouldn't fail

0

ไม่มีวิธีแก้ปัญหาสำหรับฉันจากที่นี่ดังนั้นฉันจึงพบวิธีอื่น:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

สิ่งนี้มีประโยชน์สำหรับ CI & CD วิธีนี้ข้อความแสดงข้อผิดพลาดจะถูกพิมพ์ แต่สคริปต์ทั้งหมดยังคงทำงานต่อไป


0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

ตัวอย่างการใช้สิ่งนี้เพื่อสร้างไฟล์บันทึก

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi

0

ขอบคุณสำหรับการแก้ปัญหาง่ายๆที่นี่จากด้านบน:

<particular_script/command> || true

โครงสร้างต่อไปนี้สามารถใช้สำหรับการดำเนินการเพิ่มเติม / การแก้ไขปัญหาขั้นตอนสคริปต์และตัวเลือกการควบคุมการไหลเพิ่มเติม:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

เราสามารถเบรกการกระทำเพิ่มเติมและexit 1ถ้าจำเป็น

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