Tập -e có nghĩa là gì trong một tập lệnh bash?


713

Tôi đang nghiên cứu các nội dung này preinst tập tin rằng thực thi kịch bản trước khi gói đó là giải nén từ kho lưu trữ Debian của nó (.deb) tập tin.

Kịch bản có mã sau:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

Truy vấn đầu tiên của tôi là về dòng:

set -e

Tôi nghĩ rằng phần còn lại của tập lệnh khá đơn giản: Nó kiểm tra xem trình quản lý gói Debian / Ubuntu có đang thực hiện thao tác cài đặt hay không. Nếu có, nó sẽ kiểm tra xem ứng dụng của tôi đã được cài đặt trên hệ thống chưa. Nếu có, tập lệnh sẽ in thông báo "MyApplicationName vừa được cài đặt" và kết thúc ( return 1có nghĩa là kết thúc bằng một lỗi Lỗi, phải không?).

Nếu người dùng đang yêu cầu hệ thống gói Debian / Ubuntu cài đặt gói của tôi, tập lệnh cũng xóa hai thư mục.

Điều này đúng hay tôi đang thiếu một cái gì đó?



46
lý do tại sao bạn không thể tìm thấy điều này trong google: -e trong truy vấn của bạn được hiểu là phủ định. Hãy thử truy vấn sau: bash set "-e"
Maleev

3
@twalberg Khi tôi tự hỏi mình câu hỏi tương tự, tôi đã xem xétman set
Sedat Kilinc

4
nếu bạn đang tìm cách tắt nó, hãy đổi dấu gạch ngang thành tiền tố cộng:set +e
Tom Saleeba

@twalberg nhưng hỏi người thật thì thú vị hơn nhiều so với việc chỉ yêu cầu từ robot ;-).
vdegenne

Câu trả lời:


797

Từ help set:

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

Nhưng nó được coi là thực hành xấu bởi một số (bash FAQ và irc freenode #bash FAQ tác giả). Bạn nên sử dụng:

trap 'do_something' ERR

để chạy do_somethingchức năng khi xảy ra lỗi.

Xem http://mywiki.wooledge.org/BashFAQ/105


14
Do_s Something sẽ là gì nếu tôi muốn có ngữ nghĩa tương tự như "Thoát ngay lập tức nếu một lệnh thoát với trạng thái khác không"?
CMCDragonkai

71
trap 'exit' ERR
chepner

12
Các ERRbẫy không được thừa kế bởi các chức năng vỏ, vì vậy nếu bạn có chức năng, set -o errtracehoặc set -Esẽ cho phép bạn chỉ cần đặt bẫy một lần và áp dụng nó trên toàn cầu.
ykay

31
trap 'exit' ERRlàm khác set -ekhông?
Andy

22
Nếu nó không tốt thì tại sao nó lại được sử dụng trong các gói Debian ?
phuclv

98

set -edừng thực thi tập lệnh nếu một lệnh hoặc đường ống có lỗi - ngược lại với hành vi shell mặc định, đó là bỏ qua lỗi trong tập lệnh. Nhập help setvào một thiết bị đầu cuối để xem tài liệu cho lệnh tích hợp này.


44
Nó chỉ dừng thực thi nếu lệnh cuối cùng trong đường ống có lỗi. Có một tùy chọn cụ thể của Bash, set -o pipefailcó thể được sử dụng để truyền lỗi để giá trị trả về của lệnh đường ống là khác không nếu một trong các lệnh trước xuất hiện với trạng thái khác không.
Anthony Geoghegan

2
Hãy nhớ rằng điều đó -o pipefailchỉ có nghĩa là trạng thái thoát của lệnh đầu tiên khác không (nghĩa là sai -o errexitvề thuật ngữ) của đường ống được truyền đến cuối. Các lệnh còn lại trong đường ống vẫn chạy , ngay cả với set -o errexit. Ví dụ: echo success | cat - <(echo piping); echo continues, nơi echo successđại diện cho một thành công, nhưng lệnh có thể sai lầm, sẽ in success, pipingcontinues, nhưng false | cat - <(echo piping); echo continues, với falseđại diện các lệnh tại erroring âm thầm, vẫn sẽ in pipingtrước khi thoát.
bb010g

55

Theo bash - Hướng dẫn cài đặt sẵn, nếu -e/ errexitđược đặt, trình bao thoát ngay lập tức nếu một đường ống bao gồm một lệnh đơn giản , danh sách hoặc lệnh ghép trả về trạng thái khác không.

Theo mặc định, trạng thái thoát của đường ống là trạng thái thoát của lệnh cuối cùng trong đường ống, trừ khi pipefailtùy chọn được bật (mặc định bị tắt).

Nếu vậy, trạng thái trả về của đường ống của lệnh cuối cùng (ngoài cùng bên phải) để thoát với trạng thái khác không hoặc bằng 0 nếu tất cả các lệnh thoát thành công.

Nếu bạn muốn thực hiện một cái gì đó khi thoát, hãy thử xác định trap, ví dụ:

trap onexit EXIT

nơi onexitlà chức năng của bạn để làm điều gì đó về xuất cảnh, như dưới đây được in đơn giản stack trace :

onexit(){ while caller $((n++)); do :; done; }

Thay vào đó, có tùy chọn tương tự -E/errtrace sẽ bẫy trên ERR, ví dụ:

trap onerr ERR

Ví dụ

Ví dụ về trạng thái không:

$ true; echo $?
0

Ví dụ về trạng thái khác không:

$ false; echo $?
1

Ví dụ về trạng thái phủ định:

$ ! false; echo $?
0
$ false || true; echo $?
0

Kiểm tra pipefailbị vô hiệu hóa:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

Kiểm tra với pipefailđược kích hoạt:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

54

Tôi tìm thấy bài đăng này trong khi cố gắng tìm hiểu trạng thái thoát là gì đối với tập lệnh bị hủy bỏ do a set -e. Câu trả lời không rõ ràng đối với tôi; do đó câu trả lời này. Về cơ bản, set -ehủy bỏ việc thực thi lệnh (ví dụ: tập lệnh shell) và trả về mã trạng thái thoát của lệnh không thành công (tức là tập lệnh bên trong, không phải tập lệnh bên ngoài) .

Ví dụ: giả sử tôi có tập lệnh shell outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

Mã cho inner-test.shlà:

#!/bin/sh
exit 26;

Khi tôi chạy outer-script.shtừ dòng lệnh, tập lệnh bên ngoài của tôi kết thúc bằng mã thoát của tập lệnh bên trong:

$ ./outer-test.sh
$ echo $?
26

10

Tôi tin rằng ý định là cho kịch bản trong câu hỏi để thất bại nhanh chóng.

Để tự kiểm tra điều này, chỉ cần gõ set -evào dấu nhắc bash. Bây giờ, hãy thử chạy ls. Bạn sẽ nhận được một danh sách thư mục. Bây giờ, gõ lsd. Lệnh đó không được nhận dạng và sẽ trả về mã lỗi và do đó dấu nhắc bash của bạn sẽ đóng (do set -e).

Bây giờ, để hiểu điều này trong ngữ cảnh của một 'tập lệnh', hãy sử dụng tập lệnh đơn giản này:

#!/bin/bash 
# set -e

lsd 

ls

Nếu bạn chạy nó như vậy, bạn sẽ nhận được danh sách thư mục từ lsdòng cuối cùng. Nếu bạn bỏ ghi chú set -evà chạy lại, bạn sẽ không thấy danh sách thư mục vì bash dừng xử lý một khi nó gặp lỗi từ đó lsd.


Câu trả lời này có thêm thông tin chi tiết hoặc thông tin nào chưa được đưa ra cho người khác về câu hỏi không?
Charles Duffy

6
Tôi nghĩ rằng nó cung cấp một lời giải thích rõ ràng, súc tích về chức năng không có trong các câu trả lời khác. Không có gì bổ sung, chỉ tập trung hơn các phản ứng khác.
Kallin Nagelberg 17/12/18

9

Đây là một câu hỏi cũ, nhưng không có câu trả lời nào ở đây thảo luận về việc sử dụng set -eaka set -o errexittrong các tập lệnh xử lý gói Debian. Việc sử dụng tùy chọn này là bắt buộc trong các tập lệnh này, theo chính sách Debian; mục đích rõ ràng là để tránh bất kỳ khả năng của một điều kiện lỗi chưa được xử lý.

Điều này có nghĩa là gì trong thực tế là bạn phải hiểu trong những điều kiện nào các lệnh bạn chạy có thể trả về một lỗi và xử lý rõ ràng từng lỗi đó.

Gotchas phổ biến là ví dụ diff(trả về lỗi khi có sự khác biệt) và grep(trả về lỗi khi không có kết quả khớp). Bạn có thể tránh các lỗi với xử lý rõ ràng:

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Cũng lưu ý cách chúng tôi chú ý bao gồm tên của tập lệnh hiện tại vào tin nhắn và viết thông báo chẩn đoán thành lỗi tiêu chuẩn thay vì đầu ra tiêu chuẩn.)

Nếu không có xử lý rõ ràng là thực sự cần thiết hoặc hữu ích, rõ ràng không làm gì:

diff this that || true
grep cat food || :

(Việc sử dụng :lệnh no-op của shell hơi tối nghĩa, nhưng khá phổ biến.)

Chỉ cần nhắc lại,

something || other

là tốc ký cho

if something; then
    : nothing
else
    other
fi

tức là chúng tôi nói rõ ràng othernên được chạy nếu và chỉ khi somethingthất bại. Longhand if(và các câu lệnh điều khiển luồng shell khác như while, until) cũng là một cách hợp lệ để xử lý lỗi (thực sự, nếu không, các tập lệnh shell với set -ekhông bao giờ có thể chứa các câu lệnh điều khiển luồng!)

Và ngoài ra, chỉ cần nói rõ ràng, nếu không có trình xử lý như thế này, set -esẽ khiến toàn bộ tập lệnh bị lỗi ngay lập tức với một lỗi nếu difftìm thấy sự khác biệt hoặc nếu grepkhông tìm thấy kết quả khớp.

Mặt khác, một số lệnh không tạo ra trạng thái thoát lỗi khi bạn muốn. Các lệnh thường gặp sự cố là find(trạng thái thoát không phản ánh liệu các tệp có thực sự được tìm thấy hay không) và sed(trạng thái thoát sẽ không tiết lộ liệu tập lệnh có nhận được bất kỳ đầu vào nào hay thực sự thực hiện bất kỳ lệnh nào thành công hay không). Một người bảo vệ đơn giản trong một số tình huống là chuyển sang một lệnh sẽ hét lên nếu không có đầu ra:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Cần lưu ý rằng trạng thái thoát của một đường ống là trạng thái thoát của lệnh cuối cùng trong đường ống đó. Vì vậy, các lệnh trên thực sự che dấu hoàn toàn trạng thái của findsed, và chỉ cho bạn biết liệu grepcuối cùng có thành công hay không.

(Tất nhiên, Bash có set -o pipefail; nhưng các tập lệnh gói Debian không thể sử dụng các tính năng của Bash. Chính sách này hoàn toàn ra lệnh sử dụng POSIX shcho các tập lệnh này, mặc dù điều này không phải lúc nào cũng đúng.)

Trong nhiều tình huống, đây là điều cần chú ý riêng khi mã hóa phòng thủ. Đôi khi, bạn phải đi qua một tệp tạm thời để bạn có thể xem liệu lệnh tạo ra đầu ra đó đã kết thúc thành công hay chưa, ngay cả khi thành ngữ và sự thuận tiện sẽ hướng dẫn bạn sử dụng đường ống vỏ.


đây là một câu trả lời tuyệt vời và nó thúc đẩy thực hành tốt nhất. Tôi đã có chính xác vấn đề tương tự từ lệnh GREP và tôi thực sự không muốn xóa 'set -e'
Minnie

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
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.