'Set -e' làm gì và tại sao nó có thể được coi là nguy hiểm?


147

Câu hỏi này đã xuất hiện trong một bài kiểm tra trước khi phỏng vấn và nó làm tôi phát điên. Bất cứ ai có thể trả lời điều này và đưa tôi thoải mái? Bài kiểm tra không có tham chiếu đến một vỏ cụ thể nhưng mô tả công việc là dành cho một unix sa. một lần nữa câu hỏi chỉ đơn giản là ...

'Set -e' làm gì và tại sao nó có thể được coi là nguy hiểm?


Đây là một chủ đề liên quan: stackoverflow.com/questions/64786/error-handling-in-bash/
mẹo

Câu trả lời:


154

set -e làm cho shell thoát ra nếu bất kỳ tiểu ban hoặc đường ống nào trả về trạng thái khác không.

Câu trả lời mà người phỏng vấn có lẽ đang tìm kiếm là:

Sẽ rất nguy hiểm khi sử dụng "set -e" khi tạo tập lệnh init.d:

Từ http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -

Hãy cẩn thận khi sử dụng set -e trong tập lệnh init.d. Viết các tập lệnh init.d chính xác yêu cầu chấp nhận các trạng thái thoát lỗi khác nhau khi trình nền đã chạy hoặc đã dừng mà không hủy bỏ tập lệnh init.d và các thư viện hàm init.d thông thường không an toàn để gọi với set -e có hiệu lực. Đối với tập lệnh init.d, thường không dễ sử dụng set -e và thay vào đó kiểm tra kết quả của từng lệnh một cách riêng biệt.

Đây là một câu hỏi hợp lệ theo quan điểm của người phỏng vấn vì nó đánh giá một ứng viên có kiến ​​thức làm việc về kịch bản và tự động hóa ở cấp độ máy chủ


điểm tốt. nó sẽ dừng quá trình khởi động đối với một cái gì đó có thể là lỗi kỹ thuật, nhưng không nên khiến toàn bộ tập lệnh dừng lại
Matt

Mặc dù tôi đồng ý với stackoverflow.com/questions/78497/ , tôi nghĩ rằng phong cách này phù hợp với bash để kiểm tra init ngắn và ghi nhật ký hoặc vá khỉ môi trường khác trước khi thực hiện khối lượng công việc mục tiêu. Chúng tôi sử dụng chính sách này cho hình ảnh docker của chúng tôi kịch bản init.
joshperry

33

Từ bash(1):

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

Thật không may, tôi không đủ sáng tạo để nghĩ tại sao nó lại nguy hiểm, ngoài "phần còn lại của kịch bản sẽ không được thực thi" hoặc "nó có thể có thể che giấu những vấn đề thực sự".


1
che giấu những vấn đề thực tế / khác, vâng, tôi đoán tôi có thể thấy rằng ... tôi đã vật lộn để đưa ra một ví dụ về việc nó nguy hiểm, cũng ...
cpbills

Có trang dành cho set! Tôi luôn luôn kết thúc tại builtin(1). Cảm ơn.
Jared Beck

Làm thế nào tôi biết rằng tài liệu cho setlà để hoàn thành man 1 bash? và làm thế nào bất cứ ai cũng có thể tìm thấy -etùy chọn cho settừ khóa trong một trang thủ công rất lớn? bạn không thể /-etìm kiếm ở đây
phil294

@Blauhirn sử dụnghelp set
phil294

19

Cần lưu ý rằng set -ecó thể bật và tắt cho các phần khác nhau của tập lệnh. Nó không phải được thực hiện cho toàn bộ thực thi tập lệnh. Nó thậm chí có thể được kích hoạt có điều kiện. Điều đó nói rằng, tôi không bao giờ sử dụng nó kể từ khi tôi tự xử lý lỗi (hoặc không).

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

Cũng đáng chú ý là điều này từ phần trang Bash của người đàn ông trên traplệnh cũng mô tả cách các set -echức năng trong các trường hợp nhất định.

Bẫy ERR không được thực thi nếu lệnh thất bại là một phần của danh sách lệnh ngay sau một thời gian hoặc cho đến khi từ khóa, một phần của kiểm tra trong câu lệnh if, một phần của lệnh được thực thi trong danh sách && hoặc hoặc nếu lệnh giá trị trả lại đang được đảo qua! Đây là những điều kiện tương tự được tuân theo bởi tùy chọn errexit.

Vì vậy, có một số điều kiện theo đó một trạng thái khác không sẽ không gây ra lối thoát.

Tôi nghĩ rằng nguy hiểm là không hiểu khi nào set -echơi và khi nào nó không và dựa vào nó không chính xác theo một số giả định không hợp lệ.

Vui lòng xem BashFAQ / 105 Tại sao không đặt -e (hoặc đặt -o errexit hoặc bẫy ERR) làm những gì tôi mong đợi?


Trong khi đi lệch khỏi câu hỏi một chút, câu trả lời này chính xác là những gì tôi đang tìm kiếm: tắt nó đi chỉ là một phần nhỏ của một kịch bản!
Stephan bijzitter

13

Hãy nhớ rằng đây là một bài kiểm tra cho một cuộc phỏng vấn việc làm. Các câu hỏi có thể đã được viết bởi các nhân viên hiện tại, và họ có thể sai. Điều này không hẳn là xấu, và mọi người đều mắc lỗi, và các câu hỏi phỏng vấn thường ngồi trong một góc tối mà không xem xét, và chỉ xuất hiện trong một cuộc phỏng vấn.

Hoàn toàn có khả năng 'set -e' không làm gì mà chúng tôi cho là "nguy hiểm". Nhưng tác giả của câu hỏi đó có thể lầm tưởng rằng 'set -e' là nguy hiểm, do sự thiếu hiểu biết hoặc thiên vị của chính họ. Có thể họ đã viết một kịch bản vỏ lỗi, nó bị đánh bom khủng khiếp và họ đã lầm tưởng rằng 'set -e' là để đổ lỗi, trong khi thực tế họ đã bỏ qua việc viết kiểm tra lỗi thích hợp.

Có lẽ tôi đã tham gia 40 cuộc phỏng vấn xin việc trong 2 năm qua và đôi khi người phỏng vấn hỏi những câu hỏi sai hoặc có câu trả lời sai.

Hoặc có thể đó là một câu hỏi mẹo, sẽ rất khập khiễng, nhưng không hoàn toàn bất ngờ.

Hoặc có thể đây là một lời giải thích tốt: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


1
+1 Tôi đang ở trong cùng một chiếc thuyền, và rất nhiều cuộc phỏng vấn 'kỹ thuật' mà tôi đã xử lý ít nhất là hơi sai lầm.
cpbills

1
ồ Tìm thấy tốt trong danh sách debian. +1 cho sự thiếu hiểu biết của các câu hỏi phỏng vấn. Tôi nhớ đã tranh luận một câu trả lời netback một lần vì tôi chắc chắn 100% rằng tôi đã đúng. Họ nói không. Tôi về nhà và googled nó, tôi đã đúng.
egorgry

9

set -e nói với bash, trong một tập lệnh, để thoát bất cứ khi nào bất cứ điều gì trả về giá trị trả về khác không.

Tôi có thể thấy điều đó sẽ gây phiền nhiễu như thế nào và lỗi, không chắc chắn về sự nguy hiểm, trừ khi bạn đã mở quyền trên một cái gì đó, và trước khi bạn có thể hạn chế chúng một lần nữa, tập lệnh của bạn đã chết.


Nó thật thú vị. Tôi đã không nghĩ về các quyền được mở ra trong một kịch bản chết sớm nhưng tôi có thể thấy rằng nó được coi là nguy hiểm. Điều thú vị của bài kiểm tra này là bạn không thể sử dụng bất kỳ tài liệu tham khảo nào như người đàn ông hoặc google và nếu bạn không thể trả lời đầy đủ thì đừng trả lời.
egorgry

6
điều đó thật ngớ ngẩn, tôi sẽ xem xét lại chủ nhân này ... đùa (loại). Thật tốt khi có một nền tảng kiến ​​thức vững chắc, nhưng một nửa công việc CNTT là biết / tìm / tìm thông tin ở đâu ... hy vọng họ đủ thông minh để cân nhắc điều đó khi chấm điểm ứng viên. trên một lưu ý phụ, tôi thấy / không / sử dụng cho set -e, thực tế, đối với tôi, nó nói về sự lười biếng khi bỏ qua việc kiểm tra lỗi trong tập lệnh của bạn ... vì vậy, hãy nhớ rằng, với nhà tuyển dụng này, ... nếu họ sử dụng nó rất nhiều, kịch bản của họ trông như thế nào, chắc chắn bạn sẽ phải duy trì ...
cpbills

điểm tuyệt vời @cpbills. Tôi cũng thấy không có ích gì cho tập -e trong một kịch bản và có lẽ đó là lý do tại sao nó làm tôi bối rối. Tôi muốn đăng tất cả các câu hỏi vì một số thực sự tốt và một số thực sự lập dị và ngẫu nhiên như thế này.
egorgry

Tôi đã thấy nó trong nhiều công việc định kỳ hệ thống ...
Andrew

8

set -echấm dứt tập lệnh nếu gặp mã thoát khác, ngoại trừ trong một số điều kiện nhất định. Tóm lại những nguy hiểm của việc sử dụng nó trong một vài từ: nó không hành xử theo cách mọi người nghĩ.

Theo tôi, nó nên được coi là một hack nguy hiểm tiếp tục tồn tại cho mục đích tương thích. Câu set -elệnh không biến shell từ một ngôn ngữ sử dụng mã lỗi thành ngôn ngữ sử dụng luồng điều khiển giống như ngoại lệ, nó chỉ cố gắng mô phỏng hành vi đó một cách kém.

Greg Wooledge có rất nhiều điều để nói về sự nguy hiểm của set -e:

Trong liên kết thứ hai, có nhiều ví dụ khác nhau về hành vi không trực quan và không thể đoán trước của set -e.

Một số ví dụ về hành vi không trực quan của set -e(một số được lấy từ liên kết wiki ở trên):

  • đặt -e
    x = 0
    hãy x ++
    tiếng vang "x là $ x"
    Ở trên sẽ khiến tập lệnh shell thoát sớm, bởi vì let x++trả về 0, được xử lý bởi lettừ khóa dưới dạng giá trị giả và biến thành mã thoát không khác. set -ethông báo điều này, và âm thầm chấm dứt kịch bản.
  • đặt -e
    [-d / opt / foo] && echo "Cảnh báo: foo đã được cài đặt. Sẽ ghi đè lên." > & 2
    echo "Cài đặt foo ..."
    

    Các công việc trên như mong đợi, in một cảnh báo nếu /opt/foođã tồn tại.

    đặt -e
    check_preingly_install () {
        [-d / opt / foo] && echo "Cảnh báo: foo đã được cài đặt. Sẽ ghi đè lên." > & 2
    }
    
    check_preingly_install
    echo "Cài đặt foo ..."
    

    Ở trên, mặc dù sự khác biệt duy nhất là một dòng duy nhất đã được tái cấu trúc thành một hàm, sẽ chấm dứt nếu /opt/fookhông tồn tại. Điều này là do thực tế rằng nó hoạt động ban đầu là một ngoại lệ đặc biệt đối với set -ehành vi của. Khi a && btrả về giá trị khác, nó bị bỏ qua set -e. Tuy nhiên, bây giờ là một hàm, mã thoát của hàm bằng với mã thoát của lệnh đó và hàm trả về giá trị khác sẽ âm thầm chấm dứt tập lệnh.

  • đặt -e
    IFS = $ '\ n' đọc -d '' -r -a config_vars <config
    

    Ở trên sẽ đọc các mảng config_varstừ các tập tin config. Như tác giả có thể dự định, nó chấm dứt với một lỗi nếu configbị thiếu. Vì tác giả có thể không có ý định, nó âm thầm chấm dứt nếu configkhông kết thúc trong một dòng mới. Nếu set -ekhông được sử dụng ở đây, thì config_varsnó sẽ chứa tất cả các dòng của tệp cho dù nó có kết thúc trong một dòng mới hay không.

    Người dùng của Sublime Text (và các trình soạn thảo văn bản khác xử lý các dòng mới không chính xác), hãy cẩn thận.

  • đặt -e
    
    nên_audit_user () {
        nhóm nhóm cục bộ = "$ (nhóm" $ 1 ")"
        cho nhóm trong nhóm $; làm
            if ["$ group" = kiểm toán]; sau đó trả về 0; fi
        làm xong
        trả lại 1
    }
    
    if Should_audit_user "$ user"; sau đó
        logger 'Blah'
    fi
    

    Tác giả ở đây có thể mong đợi một cách hợp lý rằng nếu vì lý do nào đó người dùng $userkhông tồn tại, thì groupslệnh sẽ thất bại và tập lệnh sẽ chấm dứt thay vì để người dùng thực hiện một số tác vụ không được kiểm tra. Tuy nhiên, trong trường hợp này, việc set -echấm dứt không bao giờ có hiệu lực. Nếu $userkhông thể được tìm thấy vì một số lý do, thay vì chấm dứt tập lệnh, should_audit_userhàm sẽ chỉ trả về dữ liệu không chính xác như thể set -ekhông có hiệu lực.

    Điều này áp dụng cho bất kỳ chức năng nào được gọi từ phần điều kiện của ifcâu lệnh, bất kể được lồng sâu đến đâu, bất kể nó được định nghĩa ở đâu, bất kể bạn có chạy lại set -ebên trong nó hay không. Việc sử dụng iftại bất kỳ điểm nào sẽ vô hiệu hóa hoàn toàn ảnh hưởng của set -echo đến khi khối điều kiện được thực thi hoàn toàn. Nếu tác giả không nhận thức được sự cố này hoặc không biết toàn bộ ngăn xếp cuộc gọi của họ trong tất cả các tình huống có thể được gọi, thì họ sẽ viết mã lỗi và ý thức bảo mật sai được cung cấp set -eít nhất là một phần cho khiển trách.

    Ngay cả khi tác giả nhận thức đầy đủ về cạm bẫy này, cách giải quyết là viết mã theo cách giống như người ta sẽ viết nó mà không cần set -e, hiển thị hiệu quả công tắc đó ít hơn vô dụng; tác giả không chỉ phải viết mã xử lý lỗi thủ công như thể set -ekhông có hiệu lực, mà sự hiện diện của set -ecó thể đã đánh lừa họ nghĩ rằng họ không phải làm vậy.

Một số nhược điểm nữa của set -e:

  • Nó khuyến khích mã cẩu thả. Trình xử lý lỗi hoàn toàn bị lãng quên, với hy vọng rằng bất cứ điều gì thất bại sẽ báo cáo lỗi theo một cách hợp lý. Tuy nhiên, với các ví dụ như let x++trên, đây không phải là trường hợp. Nếu kịch bản chết bất ngờ, nó thường âm thầm, gây cản trở việc gỡ lỗi. Nếu tập lệnh không chết và bạn mong đợi nó (xem điểm đạn trước đó), thì bạn có một lỗi tinh vi và quỷ quyệt hơn trên tay.
  • Nó dẫn mọi người vào một cảm giác an toàn sai lầm. Xem lại ifđiểm đạn -condition.
  • Những nơi mà vỏ kết thúc không nhất quán giữa các phiên bản vỏ hoặc vỏ. Có thể vô tình viết một tập lệnh hoạt động khác nhau trên một phiên bản bash cũ hơn do hành vi set -ebị điều chỉnh giữa các phiên bản đó.

set -elà một vấn đề gây tranh cãi và một số người nhận thức được các vấn đề xung quanh nó đề xuất chống lại nó, trong khi một số người khác chỉ khuyên bạn nên cẩn thận trong khi nó đang hoạt động để biết những cạm bẫy. Có nhiều người mới viết kịch bản shell đề xuất set -etrên tất cả các tập lệnh dưới dạng bắt tất cả các điều kiện lỗi, nhưng trong thực tế, nó không hoạt động theo cách đó.

set -e không thay thế cho giáo dục.


2

Tôi muốn nói rằng nó nguy hiểm vì bạn không kiểm soát dòng chảy kịch bản của bạn nữa. Tập lệnh có thể chấm dứt miễn là bất kỳ lệnh nào mà tập lệnh gọi trả về giá trị khác không. Vì vậy, tất cả những gì bạn phải làm là làm một cái gì đó thay đổi hành vi hoặc đầu ra của bất kỳ thành phần nào và bạn có thể chấm dứt tập lệnh chính. Nó có thể là một vấn đề phong cách, nhưng nó chắc chắn có hậu quả. Điều gì sẽ xảy ra nếu tập lệnh chính của bạn được đặt một số cờ và nó không phải vì nó bị chấm dứt sớm? Cuối cùng, bạn sẽ lỗi phần còn lại của hệ thống nếu nó giả sử cờ sẽ ở đó hoặc làm việc với một giá trị mặc định hoặc cũ không mong muốn.


Hoàn toàn đồng ý với câu trả lời này. Nó không thực sự nguy hiểm, nhưng đó là cơ hội để có một lối thoát sớm bất ngờ.
pboin

1
Điều này khiến tôi nhớ đến là một C ++ prof của tôi, người luôn lấy điểm từ bài tập của mình vì không có điểm vào / điểm thoát đơn trong một chương trình. Tôi nghĩ rằng đó hoàn toàn là một điều 'nguyên tắc', nhưng doanh nghiệp tập hợp này chắc chắn sẽ trình diễn cách thức và lý do tại sao mọi thứ hoàn toàn có thể vượt khỏi tầm kiểm soát. Cuối cùng, các chương trình là về kiểm soát và tính quyết định, và với việc chấm dứt sớm, bạn từ bỏ cả hai.
Marcin

0

Như một ví dụ cụ thể hơn về câu trả lời của @ Marcin đã cá nhân tôi cắn, hãy tưởng tượng có một rm foo/bar.txtdòng ở đâu đó trong kịch bản của bạn. Thường không có vấn đề lớn nếu foo/bar.txtkhông thực sự tồn tại. Tuy nhiên với set -ehiện tại kịch bản của bạn sẽ chấm dứt sớm ở đó! Úi.

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.