Bẫy bẫy ăn sâu INT INT HẠN EXIT thực sự cần thiết?


63

Nhiều ví dụ để trapsử dụng trap ... INT TERM EXITcho các nhiệm vụ dọn dẹp. Nhưng nó có thực sự cần thiết để liệt kê tất cả ba sigspec?

Hướng dẫn nói:

Nếu một TÍN HIỆU là EXIT (0) ARG được thực thi khi thoát khỏi trình bao.

mà tôi tin rằng áp dụng cho dù tập lệnh kết thúc bình thường hay nó kết thúc bởi vì nó nhận được SIGINThoặc SIGTERM. Một thí nghiệm cũng xác nhận niềm tin của tôi:

$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+  Interrupt               ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+  Terminated              ./trap-exit

Vậy thì tại sao nhiều ví dụ liệt kê tất cả INT TERM EXIT? Hoặc tôi đã bỏ lỡ một cái gì đó và có trường hợp nào một đế EXITsẽ bỏ lỡ?


3
Ngoài ra, hãy nhớ rằng với một thông số kỹ thuật như INT TERM EXITmã dọn dẹp được thực thi hai lần khi SIGTERMhoặc SIGINTđược nhận.
maxschlepzig

Câu trả lời:


19

Thông số POSIX không nói nhiều về các điều kiện dẫn đến thực thi bẫy EXIT, chỉ về môi trường của nó phải trông như thế nào khi được thực thi.

Trong vỏ tro của Busybox, kiểm tra thoát bẫy của bạn không lặp lại 'TRAP' trước khi thoát do SIGINT hoặc SIGTERM. Tôi nghi ngờ có những cái vỏ khác đang tồn tại có thể không hoạt động theo cách đó.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

3
dashcũng không bẫy chỉ EXITkhi nó nhận được SIGINT/SIGTERM.
maxschlepzig

4
zshcũng vậy - có lẽ bashlà lớp vỏ duy nhất có EXITtín hiệu khớp.
maxschlepzig

@maxschlepzig zshkhông bẫy EXITkhi nhận INT, nhưng nó không nhận khi nhận TERM. EDIT: Tôi mới nhận thấy cái này bao nhiêu tuổi ...
JoL

27

Có một sự khác biệt.

Tập lệnh này sẽ thoát khi bạn nhấn Enterhoặc gửi SIGINThoặc SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

Tập lệnh này sẽ thoát khi bạn nhấn Enter:

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Đã thử nghiệm trong sh , BashZsh . (không còn hoạt động trong sh khi bạn thêm lệnh để bẫy chạy)


Cũng có những gì @Shawn nói: AshDash không bẫy tín hiệu EXIT.

Vì vậy, để xử lý tín hiệu mạnh mẽ, tốt nhất là tránh bẫy EXIThoàn toàn và sử dụng cái gì đó như thế này:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

1
Giải pháp với dọn dẹp làm đúng - rất thanh lịch! Nó đã trở thành một thành ngữ cho các tập lệnh bash của tôi với mktempcác cuộc gọi.
Bjoern Dahlgren

2
exitcần thiết trong cleanup?
jarno

3
Điều này không hoạt động nếu bạn có lỗi shellscript trong mã của bạn khiến nó thoát sớm.
ijw

2
@ijw: Trong Bash và Ksh, bạn có thể bẫy ERRđể xử lý việc đó, nhưng nó không phải là di động .
Zaz

5
Giải pháp này không mạnh khi một vỏ khác gọi nó. Nó không xử lý chờ đợi trên lối ra hợp tác ; bạn sẽ muốn trap - INT TERM; kill -2 $$là dòng dọn dẹp cuối cùng, để nói với shell cha rằng nó thoát ra sớm. Nếu shell cha foobar.sh gọi tập lệnh của bạn (foo.sh), sau đó gọi bar.sh, bạn không muốn bar.sh thực thi nếu INT / TERM được gửi đến foo.sh. trap cleanup EXITsẽ tự động xử lý việc truyền bá này, vì vậy IMO là mạnh nhất. Điều đó cũng có nghĩa là bạn sẽ không phải gọi cleanupở cuối tập lệnh.
Nicholas Pipitone

12

Tinh chỉnh câu trả lời cuối cùng, bởi vì nó có vấn đề:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Điểm trên:

Trình xử lý INT và TERM không bỏ qua cho tôi khi tôi kiểm tra - họ xử lý lỗi sau đó trình bao trả lại cho thoát (và điều này không quá ngạc nhiên). Vì vậy, tôi đảm bảo rằng việc dọn dẹp thoát ra sau đó và trong trường hợp tín hiệu luôn sử dụng mã lỗi (và trong trường hợp khác là thoát bình thường, sẽ giữ lại mã lỗi).

Với bash, có vẻ như thoát trong trình xử lý INT cũng gọi trình xử lý EXIT, do đó tôi gỡ bỏ trình xử lý thoát và tự gọi nó (sẽ hoạt động trong mọi shell bất kể hành vi nào).

Tôi bẫy thoát vì các kịch bản shell có thể thoát trước khi chúng chạm đến đáy - lỗi cú pháp, đặt -e và trả về khác không, chỉ đơn giản là gọi thoát. Bạn không thể dựa vào một shellscript đến đáy.

SIGQUIT là Ctrl- \ nếu bạn chưa từng thử. Được bạn thưởng một phần thưởng. Vì vậy, tôi nghĩ rằng nó cũng có giá trị bẫy, ngay cả khi nó hơi tối nghĩa.

Kinh nghiệm trong quá khứ cho biết nếu bạn (như tôi) luôn nhấn Ctrl-C nhiều lần, đôi khi bạn sẽ bắt được nửa chừng phần dọn dẹp tập lệnh shell của mình, do đó, điều này hoạt động nhưng không phải lúc nào cũng hoàn hảo như bạn muốn.


2
Người gọi sẽ chỉ lấy 1 làm mã thoát, bất kể tín hiệu nào gây ra lối thoát, trong khi đó, trapngười gọi sẽ nhận được 130 cho SIGINT, 143 cho SIGTERM, v.v. Vì vậy, tôi sẽ nắm bắt và chuyển mã thoát chính xác là : sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }.
musiphil

2
Bạn có thể làm rõ mục đích của trap '' EXIT INT TERMchức năng dọn dẹp? Đây có phải là để ngăn chặn sự gián đoạn người dùng vô tình của việc dọn dẹp mà bạn đã đề cập trong đoạn cuối không? Không phải là EXITdư thừa?
Sáu
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.