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


15

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

ฉันประหลาดใจมากฉันไม่สามารถทำสิ่งนี้ได้ ( set -eไม่เพียงพอ) ตัวอย่าง:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

ผลลัพธ์ (bash-3.2.39 หรือ bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

เราไม่สามารถตรวจสอบ$?หลังจากทุกคำสั่งเพื่อตรวจจับข้อผิดพลาดทางไวยากรณ์

(ฉันคาดหวังพฤติกรรมที่ปลอดภัยเช่นนี้จากภาษาการเขียนโปรแกรมที่สมเหตุสมผล ... บางทีนี่อาจต้องรายงานเป็นข้อผิดพลาด / ต้องการทุบตีผู้พัฒนา)

ทดลองเพิ่มเติม

if ทำให้ไม่มีความแตกต่าง

กำลังลบif:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

ผลลัพธ์:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

บางทีก็เกี่ยวข้องกับการออกกำลังกาย 2 จากhttp://mywiki.wooledge.org/BashFAQ/105(( ))และมีสิ่งที่จะทำอย่างไรกับ แต่ฉันคิดว่ามันยังไม่มีเหตุผลที่จะดำเนินการต่อหลังจากข้อผิดพลาดทางไวยากรณ์

ไม่(( ))สร้างความแตกต่าง!

มันทำงานไม่ดีแม้จะไม่มีการทดสอบทางคณิตศาสตร์! เพียงสคริปต์พื้นฐานที่เรียบง่าย:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

ผลลัพธ์:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

set -eไม่เพียงพอเนื่องจากข้อผิดพลาดทางไวยากรณ์ของคุณอยู่ในifคำสั่ง ทุกที่อื่นควรยกเลิกสคริปต์
jordanm

@jordanm ตกลงนี่เป็นคำอธิบายset -eได้ว่าทำไมไม่ทำงาน แต่คำถามของฉันยังคงสมเหตุสมผล เป็นไปได้ไหมที่จะยกเลิกข้อผิดพลาดทางไวยากรณ์?
imz - Ivan Zakharyaschev

@jordanm ลบ "if"; ทำให้ไม่มีความแตกต่าง (อัปเดตคำถามของฉัน)
imz - Ivan Zakharyaschev

คำตอบ:


9

การรวมทั้งหมดเข้าไปในฟังก์ชันดูเหมือนจะเป็นการหลอกลวง:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

ผลลัพธ์:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

แม้ว่าฉันจะไม่มีเงื่อนงำทำไม - บางทีคนอื่นสามารถอธิบายได้?


2
ตอนนี้นิยามฟังก์ชันของคุณถูกวิเคราะห์และประเมินผลแล้วและล้มเหลว
tripleee

ทางออกที่ดี! BTW มันไม่ได้ยกเลิกโปรแกรมทั้งหมดในกรณีนี้ด้วย ฉันได้ผนวกecho 'Bad2: has not aborted the execution after bad main!'เป็นตัวอย่างสุดท้ายของคุณและผลลัพธ์คือ: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: บรรทัด 6: #: ข้อผิดพลาดทางไวยากรณ์: ตัวถูกดำเนินการที่คาดหวัง ( โทเค็นข้อผิดพลาดคือ "#") Bad2: ไม่ได้ยกเลิกการดำเนินการหลังจากที่ main ไม่ดี! $
imz - Ivan Zakharyaschev

แต่เราไม่ควรต่อท้ายบรรทัดง่ายๆเราควรใส่ทุกอย่างไว้ในฟังก์ชั่น
imz - Ivan Zakharyaschev

@tripleee ใช่ดูเหมือนว่าการแยกฟังก์ชันล้มเหลวดังนั้นมันจึงไม่สมบูรณ์ แต่โปรแกรมทั้งหมดไม่ได้ถูกยกเลิกจริง ๆ ในกรณีนี้ (ดังนั้นจึงไม่ใช่ผลของ exit-on-error อาจเป็นไปได้)
imz - Ivan Zakharyaschev

6

set -eคุณอาจจะเข้าใจผิดเกี่ยวกับความหมายของแท้ การอ่านเอาต์พุตที่help setแสดงอย่างระมัดระวัง

-e  Exit immediately if a command exits with a non-zero status.

ดังนั้น-eเกี่ยวกับสถานะการออกของคำสั่งที่ไม่ใช่ศูนย์ไม่ใช่เกี่ยวกับข้อผิดพลาดทางไวยากรณ์ในสคริปต์ของคุณ

โดยทั่วไปถือว่าเป็นการใช้งานที่ไม่ถูกต้องset -eเนื่องจากข้อผิดพลาดทั้งหมด (เช่นผลตอบแทนที่ไม่เป็นศูนย์ทั้งหมดจากคำสั่ง) ควรได้รับการจัดการอย่างชาญฉลาดโดยสคริปต์ (คิดว่าสคริปต์ที่มีประสิทธิภาพไม่ใช่ตัวที่ผิดปกติหลังจากที่คุณป้อนชื่อไฟล์ด้วย ช่องว่างหรือที่ขึ้นต้นด้วย hypen)

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

ฉันแค่หวังว่าฉันจะชี้แจงให้ชัดเจนset -e!

เกี่ยวกับความปรารถนาของคุณ:

ฉันคาดหวังพฤติกรรมที่ปลอดภัยเช่นนี้จากภาษาการเขียนโปรแกรมที่สมเหตุสมผล ... บางทีนี่อาจจะต้องรายงานเป็นข้อผิดพลาด / ต้องการทุบตีนักพัฒนา

คำตอบคือไม่แน่นอน! สิ่งที่คุณสังเกตเห็น ( set -eไม่มีการตอบสนองตามที่คุณคาดหวัง) เป็นเอกสารที่ดีมาก


ฉันหมายถึงการขาดคุณสมบัติดังกล่าวเป็นปัญหา ฉันไม่ต้องการที่จะมุ่งเน้นset -e- มันใกล้กับเป้าหมายของฉันเพียงเล็กน้อยนั่นคือสาเหตุที่มีการพูดถึงและใช้ที่นี่ คำถามของฉันไม่เกี่ยวกับset -eมันเป็นเรื่องของความไม่ปลอดภัยของการทุบตีหากไม่สามารถยกเลิกได้เนื่องจากข้อผิดพลาดทางไวยากรณ์ ฉันกำลังมองหาวิธีที่จะทำให้ยกเลิกข้อผิดพลาดทางไวยากรณ์ได้เสมอ
imz - Ivan Zakharyaschev

4

คุณสามารถทำให้สคริปต์ตรวจสอบตัวเองโดยใส่สิ่งที่ชอบ

bash -n "$0"

ใกล้ส่วนบนสุดของสคริปต์ - หลังset -eแต่ก่อนหน้ารหัสที่สำคัญ

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


ยอดเยี่ยม หรือไม่มี set -e: bash -n "$0" || exit
Daniel S

0

ก่อนอื่น, (( ))in in bash จะถูกใช้เป็นการคำนวณทางคณิตศาสตร์, เพื่อไม่ให้ใช้ใน if ... ใช้ []สำหรับการนั้น

ประการที่สอง${a[#]}คือแปลกและเป็นสาเหตุที่ทำให้เกิดข้อผิดพลาด ... #ไม่มีความหมายของอาร์เรย์

ฉันไม่รู้ว่าคุณต้องการทำอะไรกับสิ่งนี้ แต่ฉันคิดว่าคุณต้องการทราบจำนวนสาขาดังนั้นคุณต้องการ${#a[*]}แทน

ในที่สุดเมื่อเปรียบเทียบจำนวนเต็ม-eqแนะนำให้ใช้มากกว่า==(ใช้สำหรับสตริง) ==ยังจะทำงาน แต่-eqขอแนะนำ

ดังนั้นคุณต้องการ:

if [ ${#a[*]} -eq 2 ]; then 

4
ที่ไม่เป็นความจริง. เป็นเรื่องปกติที่จะใช้((คำหลักกับifคำหลัก ตัวอย่างเช่นif (( 5 * $b > 53 )). ถ้าคุณกำลังเล็งสำหรับการพกพาด้วยเปลือกหอยเก่า[[เป็นที่นิยมโดยทั่วไป[มากกว่า

2
ใช่ฉันเห็นด้วยกับ @Evan - [[และ((ได้รับการออกแบบมาเป็นพิเศษสำหรับการทดสอบที่มีน้ำหนักเบาที่จะใช้กับ "if" ฯลฯ - ซึ่งแตกต่างจาก[พวกเขาไม่เคยวางกระบวนการย่อยเพื่อประเมินสภาพ
imz - Ivan Zakharyaschev

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