Làm cách nào để bash hủy bỏ việc thực thi tập lệnh về lỗi cú pháp?


15

Để an toàn, tôi muốn bash hủy bỏ việc thực thi tập lệnh nếu nó gặp lỗi cú pháp.

Thật ngạc nhiên, tôi không thể đạt được điều này. ( set -ekhông đủ.) Ví dụ:

#!/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!'

Kết quả (bash-3.2.39 hoặc 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!
$ 

Chà, chúng tôi không thể kiểm tra $?sau mỗi câu lệnh để bắt lỗi cú pháp.

(Tôi mong đợi hành vi an toàn như vậy từ một ngôn ngữ lập trình hợp lý ... có lẽ điều này phải được báo cáo là lỗi / muốn làm bash các nhà phát triển)

Thêm thí nghiệm

if không có sự khác biệt

Đang xóa 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!'

Kết quả:

$ ./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!
$ 

Có lẽ, nó liên quan đến bài tập 2 từ http://mywiki.wooledge.org/BashFAQ/105 và có liên quan (( )). Nhưng tôi thấy vẫn không hợp lý khi tiếp tục thực hiện lỗi sau cú pháp.

Không, (( ))không có sự khác biệt!

Nó cư xử xấu ngay cả khi không có bài kiểm tra số học! Chỉ là một kịch bản đơn giản, cơ bản:

#!/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!'

Kết quả:

$ ./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 -elà không đủ vì lỗi cú pháp của bạn nằm trong một ifcâu lệnh. Bất cứ nơi nào khác nên hủy bỏ kịch bản.
jordanm

@jordanm Ok, đây có thể là một lời giải thích tại sao set -ekhông hoạt động. Nhưng câu hỏi của tôi vẫn có ý nghĩa. Có thể hủy bỏ bất kỳ lỗi cú pháp?
imz - Ivan Zakharyaschev

@jordanm Đã xóa "nếu"; làm cho không có sự khác biệt (cập nhật câu hỏi của tôi).
imz - Ivan Zakharyaschev

Câu trả lời:


9

Gói toàn bộ vào một chức năng dường như thực hiện thủ thuật:

#!/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 "$@"

Kết quả:

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

Mặc dù tôi không có manh mối tại sao - có lẽ người khác có thể giải thích?


2
Bây giờ định nghĩa hàm của bạn được phân tích cú pháp và đánh giá, và nó thất bại.
tripleee

Giải pháp tốt đẹp! BTW, nó cũng không hủy bỏ toàn bộ chương trình trong trường hợp này. Tôi đã thêm vào echo 'Bad2: has not aborted the execution after bad main!'ví dụ cuối cùng của bạn và kết quả đầu ra là: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: dòng 6: #: lỗi cú pháp: toán hạng dự kiến ​​( mã thông báo lỗi là "#") Bad2: chưa hủy bỏ việc thực thi sau lỗi chính! $
imz - Ivan Zakharyaschev

Nhưng chúng ta không nên nối một dòng đơn giản sau đó, chúng ta nên đặt mọi thứ vào trong một hàm.
imz - Ivan Zakharyaschev

@tripleee Có, có vẻ như việc phân tích chức năng không thành công, vì vậy nó không hoàn thành, nhưng toàn bộ chương trình không bị hủy bỏ thực sự trong trường hợp này (vì vậy, đó có thể không phải là lỗi thoát khỏi lỗi).
imz - Ivan Zakharyaschev

6

Bạn có thể đang hiểu sai về ý nghĩa thực sự của set -e. Đọc kỹ đầu ra của help setchương trình:

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

Vì vậy, -evề trạng thái thoát của các lệnh là khác không, không phải về lỗi cú pháp trong tập lệnh của bạn.

Nói chung, nó được coi là thực hành xấu khi sử dụng set -e, bởi vì tất cả các lỗi (nghĩa là tất cả các trả về khác không từ các lệnh) nên được xử lý thông minh bởi tập lệnh (nghĩ rằng tập lệnh mạnh mẽ, không phải là lỗi phát sinh sau khi bạn nhập tên tệp bằng một không gian hoặc bắt đầu bằng một hypen).

Tùy thuộc vào loại lỗi cú pháp, tập lệnh thậm chí có thể không được thực thi. Tôi không đủ hiểu biết về bash để nói chính xác loại lỗi cú pháp nào (nếu chỉ có thể được phân loại) có thể dẫn đến việc hủy bỏ tập lệnh ngay lập tức hay không. Có thể một số bậc thầy Bash sẽ tham gia và làm rõ mọi thứ.

Tôi chỉ hy vọng tôi làm rõ set -e tuyên bố!

Về điều ước của bạn:

Tôi mong đợi hành vi an toàn như vậy từ một ngôn ngữ lập trình hợp lý ... có lẽ điều này phải được báo cáo như là một lỗi / muốn làm hỏng các nhà phát triển

Câu trả lời chắc chắn là không! như những gì bạn đã quan sát ( set -ekhông phản hồi như bạn mong đợi) trên thực tế là tài liệu rất tốt.


Tôi có nghĩa là sự vắng mặt của tính năng như vậy là một vấn đề. Tôi không muốn tập trung vào set -e- nó chỉ hơi gần với mục tiêu của tôi, đó là lý do tại sao nó được đề cập và sử dụng ở đây. Câu hỏi của tôi không phải set -elà về vấn đề an toàn của bash nếu không thể thực hiện để hủy bỏ các lỗi sytax. Tôi đang tìm cách để làm cho nó luôn hủy bỏ các lỗi cú pháp.
imz - Ivan Zakharyaschev

4

Bạn có thể làm cho kịch bản tự kiểm tra bằng cách đặt một cái gì đó như

bash -n "$0"

gần đầu tập lệnh - sau set -e nhưng trước bất kỳ đoạn mã quan trọng nào.

Tôi phải nói rằng điều này không cảm thấy rất mạnh mẽ, nhưng nếu nó hiệu quả với bạn, có lẽ nó được chấp nhận.


Thông minh! Hoặc, không có set -e: bash -n "$0" || exit
Daniel S

0

Đầu tiên, (( ))bash in được sử dụng như các phép tính số học, không sử dụng trong if ... sử dụng []cho điều đó.

Thứ hai, điều ${a[#]}này thật kỳ lạ và đó là lý do tại sao lại đưa ra lỗi ... #không có ý nghĩa mảng nào

Tôi không biết bạn muốn làm gì với điều này, nhưng tôi giả sử bạn muốn biết số lượng trường, vì vậy bạn muốn ${#a[*]}thay thế

Cuối cùng, khi so sánh các số nguyên, -eqkhuyến nghị trên ==(được sử dụng cho chuỗi). ý ==chí cũng có tác dụng, nhưng-eq được khuyến khích.

vậy bạn muốn:

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

4
Đo không phải sự thật. Việc sử dụng ((từ khóa với từ khóa là điều phổ biến if. Ví dụ , if (( 5 * $b > 53 )). Trừ khi bạn đang nhắm đến tính di động với vỏ cũ, [[thường thích hợp hơn [.

2
Có, tôi đồng ý với @Evan - [[((được thiết kế cụ thể dưới dạng các thử nghiệm nhẹ được sử dụng với "nếu", v.v. - không giống như [, chúng không bao giờ sinh ra một quy trình con để đánh giá tình trạng.
imz - Ivan Zakharyaschev

1
[là một bash dựng sẵn. Shell cũ hơn mong đợi nó là chương trình riêng của mình mặc dù.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.