Trạng thái thoát Bash không bị bắt mặc dù đã đặt -e và / hoặc bẫy đang hoạt động


8

Ai đó có thể giải thích hành vi bash / set -e trên đoạn mã dưới đây không?

#!/bin/bash

# Comment if you want to test the trap only
set -e -o pipefail -u -E

# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR

function reproduce() {
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
}

reproduce

# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.

status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
    echo "FIXME: why was status code not caught by set -e ?"
    echo "Executing false to prove set -e is still active"
    false
    # If the following is not executed then it proves -e is still active
    echo "set -e not active !!!!!"
    exit 2
fi

Đây là những gì có được khi thực hiện nó:

$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT

Kiểm tra mã thoát

$ echo $?
1

Phiên bản Bash

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

Sinh sản cũng với

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

Ghi chú bổ sung, liên quan đến ý kiến ​​(dù sao cũng nhờ tất cả các đề xuất):

  • Bình luận bẫy không thay đổi hành vi kỳ lạ quan sát được
  • Xóa bộ -e để chỉ giữ bẫy cũng kích hoạt bẫy

1
Tôi sẽ không kết hợp set -evới trap. trapđược gọi do lỗi và "echo ERROR CAUGHT" được gọi. Tôi có ấn tượng trapcó độ ưu tiên cao hơn set -e. Cũng theo Bash FAQ tôi nghĩ set -elà không khuyến khích, vui lòng kiểm tra mywiki.wooledge.org/BashFAQ/105 .
stephanmg

bẫy bình luận không thay đổi bất cứ điều gì
raphael.glon

Tôi chỉ đơn giản là sử dụng trapcơ chế thay thế, ví dụ trap "exit 2" ERR. Ngoài ra, đối với tôi, tập lệnh của bạn chỉ in "TÌNH TRẠNG 0". Có vẻ như bẫy ERR không được kế thừa bởi các hàm shell, điều này có giúp ích set -o errtracegì không? Nếu không, hãy xem liên kết của tôi ở trên về lý do tại sao bạn nên tránh set -eở nơi đầu tiên.
stephanmg

Câu trả lời:


3

Hãy đơn giản hóa nó; số lượng mã tối thiểu cần thiết để tái tạo vấn đề bạn đang xử lý là

set -e
: $((+)) # arithmetic expansion error
echo survived

Theo tiêu chuẩn, điều này không bao giờ nên in survived, nó nói rằng một vỏ POSIX chạy không tương tác sẽ ngay lập tức thoát ra khi có lỗi mở rộng . Nhưng dường như Bash không nghĩ vậy. Mặc dù sự khác biệt này không được ghi lại rõ ràng trong trang man, nhưng trong phần mô tả về chế độ POSIX, nó nói

  1. Các shell không tương tác thoát nếu một lỗi cú pháp trong mở rộng số học dẫn đến một biểu thức không hợp lệ.

Chúng ta có thể nói điều này có nghĩa là trong chế độ vận hành mặc định của nó, phiên Bash không tương tác không thoát khỏi lỗi đó, nhưng như bạn đã nhận ra, nó cũng không kích hoạt cơ chế errexit hoặc bẫy ERR. Thay vào đó, nó gán giá trị khác không cho $?di chuyển trên.

Để khắc phục điều này và có được hành vi mong đợi, bạn nên xác định reproducenhư sau

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)

Bằng cách này, lỗi mở rộng sẽ diễn ra trong một lớp con và khiến nó thoát ra với trạng thái khác không, do đó, errexit và bẫy sẽ có thể bắt được nó.


Theo yêu cầu của dash-o, đây là phiên bản đặt acho môi trường thực thi hiện tại khi biểu thức hợp lệ

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}

2

Nhìn bề ngoài, có vẻ như bash sẽ không kích hoạt bẫy trên các lỗi khác nhau của SYNTAX. Chỉ khi lệnh (bên ngoài, tích hợp) được thực thi (và trả về giá trị khác không), bẫy ERR sẽ được kích hoạt.

Từ trang người đàn ông:

Nếu một sigspec là ERR, lệnh arg được thực thi bất cứ khi nào một đường ống (có thể bao gồm một lệnh đơn giản), một danh sách hoặc lệnh ghép trả về trạng thái thoát khác không, tuân theo các điều kiện sau ...

Bẫy ERR chỉ áp dụng cho PIPELINE . Nếu bash xác định một lỗi cú pháp, nó sẽ hủy bỏ trước khi thực hiện đường ống, do đó KHÔNG có bẫy. Mặc dù tài liệu của anh ta cho '-e' chỉ định cùng một điều kiện ( if a pipeline ... exit with non-zero status), hành vi được quan sát là khác nhau.

Nếu bạn thử các bản mở rộng khác - ví dụ: mở rộng lệnh - bẫy được kích hoạt, vì có thực thi đường ống:

  • a = $ (lệnh xấu)
  • a = $ ([)

Nếu sử dụng thử các lỗi cú pháp khác nhau trong mở rộng số học, bẫy không được kích hoạt - không có đường ống dẫn.

  • a = $ ((2+))
  • a = $ ((2 @))

Ngoài ra, bash khác Cú pháp lỗi không kích hoạt cái bẫy: (), [[ ]].

Tôi không thể tìm thấy giải pháp không yêu cầu thay đổi sâu rộng đối với tập lệnh nguồn. Có thể gửi yêu cầu lỗi / tính năng với nhóm bash không?


2
Thực tế thú vị: thực hiện nó trong một subshell ( a=$((1109962735 - hello=12272 + 1)) )hoặc ( reproduce )thực hiện bẫy.
KamilCuk

Thật không may, khi biểu thức hợp lệ, asẽ không được đặt.
dash-o
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.