Sử dụng mã được bảo lưu trên nền tảng cho các trạng thái thoát của tập lệnh shell


15

Gần đây tôi đã xem qua danh sách Mã thoát này với ý nghĩa đặc biệt từ Hướng dẫn Bash-Scripting nâng cao. Họ đề cập đến các mã này như được bảo lưu và khuyến nghị rằng:

Theo bảng trên, mã thoát 1-2, 126-165 và 255 có ý nghĩa đặc biệt và do đó nên tránh các tham số thoát do người dùng chỉ định.

Cách đây một thời gian, tôi đã viết một đoạn script sử dụng các mã trạng thái thoát sau:

  • 0 - thành công
  • 1 - tên máy chủ không chính xác
  • 2 - đối số không hợp lệ được chỉ định
  • 3 - không đủ đặc quyền người dùng

Khi tôi viết tập lệnh, tôi không biết bất kỳ mã thoát đặc biệt nào, vì vậy tôi chỉ bắt đầu từ 1 cho điều kiện lỗi đầu tiên và tăng trạng thái thoát cho từng loại lỗi liên tiếp.

Tôi đã viết kịch bản với ý định rằng ở giai đoạn sau, nó có thể được gọi bởi các tập lệnh khác (có thể kiểm tra mã thoát khác không). Tôi chưa thực sự làm điều đó; cho đến nay tôi chỉ chạy tập lệnh từ trình vỏ tương tác của mình (Bash) và tôi đã tự hỏi điều gì / nếu có bất kỳ vấn đề nào có thể gây ra bằng cách sử dụng mã thoát tùy chỉnh của tôi. Đề xuất có liên quan / quan trọng như thế nào từ Hướng dẫn Bash-Scripting nâng cao?

Tôi không thể tìm thấy bất kỳ lời khuyên chứng thực nào trong tài liệu Bash; phần của nó trên Trạng thái thoát chỉ đơn giản là liệt kê các mã thoát được Bash sử dụng nhưng không nói rõ rằng bất kỳ mã nào trong số này được bảo lưu hoặc cảnh báo không sử dụng chúng cho các tập lệnh / chương trình của riêng bạn.


6
Tôi và những người khác, coi ABSG thường có chất lượng thấp. Theo tôi, tác giả của trang mà bạn liên kết đang đưa ra một xác nhận không được hỗ trợ rằng các mã thoát được liệt kê được bảo lưu dựa trên thực tế là chính trình bao sử dụng chúng cho các ý nghĩa cụ thể. Đã có những nỗ lực để tạo ra các tiêu chuẩn cho các kịch bản, không ai trong số đó có bất kỳ thành công nào. Điều quan trọng là ghi lại các mã lỗi bạn chọn để người tiêu dùng tập lệnh của bạn (ví dụ các tập lệnh khác) biết phải làm gì dựa trên chúng.
Tạm dừng cho đến khi có thông báo mới.

@DennisWilliamson Nếu bạn đăng bình luận của bạn dưới dạng câu trả lời, tôi rất vui lòng nâng cấp nó; Tôi đã bình chọn tất cả các câu trả lời khác khi tôi thấy mỗi câu trả lời đều hữu ích. Mặc dù câu trả lời của bạn có nội dung tương tự với David King (và ở mức độ thấp hơn của Zwol), bạn nói rõ rằng không có bằng chứng nào cho khẳng định trong trích dẫn của ABSG.
Anthony G - công lý cho Monica

1
Cảm ơn lời đề nghị, nhưng tôi tin rằng bình luận của tôi nên giữ nguyên như vậy.
Tạm dừng cho đến khi có thông báo mới.

Kể từ đó tôi đã phát hiện ra rằng đặc tả POSIX bao gồm lời khuyên tương tự vì vậy tôi đã thêm thông tin đó vào câu trả lời của riêng tôi (chứa kết quả nghiên cứu của tôi kể từ khi hỏi câu hỏi này).
Anthony G - công lý cho Monica

Câu trả lời:


10

Đã có một số nỗ lực để chuẩn hóa ý nghĩa của mã thoát quy trình. Ngoài một trong những bạn đề cập, tôi biết:

  • BSD có sysexits.hđịnh nghĩa cho các giá trị từ 64 trở lên.

  • Các greptài liệu GNU thoát mã 0 có nghĩa là ít nhất một trận đấu được tìm thấy, 1 có nghĩa là không tìm thấy kết quả khớp nào và 2 có nghĩa là đã xảy ra lỗi I / O; quy ước này rõ ràng cũng hữu ích cho các chương trình khác mà sự khác biệt giữa "không có gì sai nhưng tôi không tìm thấy gì" và "xảy ra lỗi I / O" là có ý nghĩa.

  • Nhiều triển khai của chức năng thư viện C systemsử dụng mã thoát 127 để chỉ ra rằng chương trình không tồn tại hoặc không khởi động được.

  • Trên Windows, NTSTATUScác mã (được phân tán bất tiện trên toàn bộ không gian số 32 bit) có thể được sử dụng làm mã thoát, đặc biệt là các mã chỉ ra một quy trình đã bị chấm dứt do hành vi sai trái thảm khốc (ví dụ STATUS_STACK_OVERFLOW).

Bạn không thể tin tưởng vào bất kỳ chương trình nhất định nào tuân theo bất kỳ một trong những quy ước cụ thể nào. Quy tắc đáng tin cậy duy nhất là mã thoát 0 là thành công và bất cứ điều gì khác là một loại thất bại. (Lưu ý rằng C89 của EXIT_SUCCESSkhông đảm bảo để có giá trị bằng không, tuy nhiên, exit(0)phải cư xử y hệt exit(EXIT_SUCCESS). Ngay cả khi giá trị không giống nhau)


Cảm ơn. Thật khó để chọn một câu trả lời cho những câu trả lời khác nhưng tôi chấp nhận câu trả lời này vì nó đã trả lời câu hỏi của tôi trong khi cũng cung cấp một hương vị rộng lớn của các mã thoát khác nhau được sử dụng (với các liên kết có liên quan): nó xứng đáng hơn 3 lần nâng cấp nó hiện có.
Anthony G - công lý cho Monica

11

Không có mã thoát nào có ý nghĩa đặc biệt, nhưng giá trị trong $?có thể có ý nghĩa đặc biệt.

Cách Bourne Shell và ksh93 xử lý và chuyển tiếp mã thoát và các tình huống lỗi cho biến shell $?là vấn đề. Trái với những gì bạn liệt kê, chỉ các giá trị sau $?có ý nghĩa đặc biệt:

  • 126 Không thể thực thi nhị phân mặc dù nó tồn tại
  • 127 Nhị phân đã chỉ định không tồn tại
  • 128 trạng thái thoát là == 0 nhưng vẫn tồn tại một số vấn đề không xác định

Ngoài ra, có một lớp mã không xác định và phạm vi $?mã cụ thể của nền tảng > 128 được dành riêng cho một chương trình bị gián đoạn bởi tín hiệu:

  • Bourne Shell bash và ksh88 sử dụng số tín hiệu 128 +
  • ksh93 sử dụng số tín hiệu 256 +.

Các giá trị khác không gây ra vấn đề vì chúng có thể được phân biệt với các $?giá trị đặc biệt .

Cụ thể, các giá trị 1 và 2 không được sử dụng cho các điều kiện đặc biệt mà chỉ là các mã thoát được sử dụng bởi các lệnh dựng sẵn có thể hoạt động giống nhau khi chúng không có nội dung. Vì vậy, dường như con trỏ đến hướng dẫn tập lệnh bash mà bạn cung cấp không phải là một hướng dẫn tốt vì nó chỉ liệt kê các mã được sử dụng bởi bash mà không nhận xét liệu một mã cụ thể có phải là giá trị đặc biệt nên tránh cho các tập lệnh riêng hay không.

Các phiên bản mới hơn của Bourne Shell sử dụng waitid()thay vì waitpid()chờ chương trình thoát ra và waitid()(được giới thiệu năm 1989 cho SVr4) sử dụng giao diện tòa nhà tốt hơn (tương tự như UNOS đã sử dụng năm 1980).

Vì các phiên bản Bourne Shell mới hơn mã hóa lý do thoát trong một biến riêng biệt ${.sh.code}/ ${.sh.codename}hơn mã thoát trong ${.sh.status}/ ${.sh.termsig}, hãy xem http://schillix.sourceforge.net/man/man1/bosh.1.html , mã thoát không bị quá tải với các trạng thái đặc biệt và, do kết quả của việc sử dụng `Waitid (), Bourne Shell hiện hỗ trợ trả về tất cả 32 bit của mã thoát - không chỉ 8 bit thấp.

BTW: cẩn thận không exit(256)hoặc tương tự từ chương trình C hoặc tập lệnh shell, vì điều này dẫn đến $?việc được hiểu là 0 trong trình bao cổ điển.


2
BTW: Tôi đã tạo một báo cáo lỗi chống lại FreeBSD và nhân Linux cho waitid()lỗi này vào khoảng cuối tháng 5. Người FreeBSD đã khắc phục sự cố trong vòng 20 giờ, người Linux không quan tâm đến việc sửa lỗi của họ. ... và người Cygwin nói rằng họ là lỗi do lỗi tương thích với Linux ;-)
schily 10/11/2015

2
Hành vi này được yêu cầu bởi Thông số kỹ thuật Unix đơn. Có giá trị 32 bit, vâng, nhưng giá trị đó chứa bitfield 8 bit chứa 8 bit thấp của giá trị từ đó _exit. Vui lòng liên kết báo cáo lỗi FreeBSD mà bạn đang đề cập, có thể tôi đang hiểu sai vấn đề bạn mô tả.
Random832

2
OP đã gắn thẻ câu hỏi bằng bash và đề cập đến Bash trong văn bản của câu hỏi. Bash là một vỏ có nguồn gốc Bourne. Nó không hỗ trợ ${.sh.}các biến. Tuy nhiên, sự thật là bạn nói "Bourne" chứ không phải "Bourne bắt nguồn" (mặc dù bạn bao gồm ksh93).
Tạm dừng cho đến khi có thông báo mới.

2
Câu trả lời này dường như rất cụ thể đối với biến thể cụ thể của bạn về một số Unix có nguồn gốc SVR4. Xin hãy nói rõ hơn về những gì là di động và những gì không, hãy nhớ rằng không có thứ gọi là "vỏ" Bourne, trừ khi bạn có nghĩa là cái đã có trong V7.
zwol

4
Ngược lại, tôi tin rằng chính bạn đang nhấn mạnh phạm vi biến thể ở đây, đặc biệt là biến thể lịch sử. Bạn làm cho nó có vẻ như /bin/shcó thể được dựa vào để hành xử một cách nhất quán các mã thoát đặc biệt này đa nền tảng, điều này không đúng. (Tôi không quan tâm liệu có bất kỳ hệ thống cụ thể nào /bin/shcó thể được gọi là "vỏ Bourne thực sự" hay không. Điều quan trọng hơn là phải biết rằng không có cái nào trong POSIX, và hầu hết những thứ bạn gọi là "hệ thống Unix thực" đều không ' /bin/shdù sao cũng cung cấp một tiêu chuẩn tuân thủ POSIX .)
zwol

6

Đối với kịch bản lệnh shell, đôi khi tôi nhập nguồn shell tương đương sysexist.hvới mã thoát dành riêng cho shell (có tiền tố S_EX_), mà tôi đã đặt tênexit.sh

Về cơ bản là:

EX_OK=0 # successful termination 
EX__BASE=64     # base value for error messages 
EX_USAGE=64     # command line usage error 
EX_DATAERR=65   # data format error 
EX_NOINPUT=66   # cannot open input 
EX_NOUSER=67    # addressee unknown 
EX_NOHOST=68    # host name unknown 
EX_UNAVAILABLE=69       # service unavailable 
EX_SOFTWARE=70  # internal software error 
EX_OSERR=71     # system error (e.g., can't fork) 
EX_OSFILE=72    # critical OS file missing 
EX_CANTCREAT=73 # can't create (user) output file 
EX_IOERR=74     # input/output error 
EX_TEMPFAIL=75  # temp failure; user is invited to retry 
EX_PROTOCOL=76  # remote error in protocol 
EX_NOPERM=77    # permission denied 
EX_CONFIG=78    # configuration error 
EX__MAX=78      # maximum listed value 

#System errors
S_EX_ANY=1      #Catchall for general errors
S_EX_SH=2       #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute         Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit       exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)                                                                                        
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)                               
#255*   Exit status out of range        exit -1 exit takes only integer args in the range 0 - 255              
S_EX_HUP=129                                                                                                   
S_EX_INT=130   
#...

Và có thể được tạo bằng:

#!/bin/sh
src=/usr/include/sysexits.h
echo "# Generated from \"$src\"" 
echo "# Please inspect the source file for more detailed descriptions"
echo
< "$src" sed -rn 's/^#define  *(\w+)\s*(\d*)/\1=\2/p'| sed 's:/\*:#:; s:\*/::'
cat<<'EOF'

#System errors
S_EX_ANY=1  #Catchall for general errors
S_EX_SH=2   #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute     Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit   exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)
#255*   Exit status out of range    exit -1 exit takes only integer args in the range 0 - 255
EOF
$(which kill) -l |tr ' ' '\n'| awk '{ printf "S_EX_%s=%s\n", $0, 128+NR; }'

Mặc dù vậy, tôi không sử dụng nó nhiều, nhưng những gì tôi sử dụng là hàm shell đảo ngược mã lỗi thành các định dạng chuỗi của chúng. Tôi đã đặt tên cho nó exit2str. Giả sử bạn đã đặt tên cho trình exit.shtạo ở trên exit.sh.sh, mã cho exit2strcó thể được tạo bằng ( exit2str.sh.sh):

#!/bin/sh
echo '
exit2str(){
  case "$1" in'
./exit.sh.sh | sed -nEe's|^(S_)?EX_(([^_=]+_?)+)=([0-9]+).*|\4) echo "\1\2";;|p'
echo "
  esac
}"

Tôi sử dụng cái này trong PS1shell tương tác của mình để sau mỗi lệnh tôi chạy, tôi có thể thấy trạng thái thoát của nó và dạng chuỗi của nó (nếu nó có dạng chuỗi đã biết):

[15:58] pjump@laptop:~ 
(0=OK)$ 
[15:59] pjump@laptop:~ 
(0=OK)$ fdsaf
fdsaf: command not found
[15:59] pjump@laptop:~ 
(127=S_NOENT)$ sleep
sleep: missing operand
Try 'sleep --help' for more information.
[15:59] pjump@laptop:~ 
(1=S_ANY)$ sleep 100
^C
[15:59] pjump@laptop:~ 
(130=S_INT)$ sleep 100
^Z
[1]+  Stopped                 sleep 100
[15:59] pjump@laptop:~ 
(148=S_TSTP)$

Để có được những thứ này, bạn cần một hàm không thể chấp nhận được cho hàm exit2str:

$ ./exit2str.sh.sh > exit2str.sh #Place this somewhere in your PATH

và sau đó sử dụng nó trong của bạn ~/.bashrcđể lưu và dịch mã thoát trên mỗi dấu nhắc lệnh và hiển thị dấu nhắc của bạn ( PS1):

    # ...
    . exit2str.sh
PROMPT_COMMAND='lastStatus=$(st="$?"; echo -n "$st"; str=$(exit2str "$st") && echo "=$str"); # ...'
    PS1="$PS1"'\n($lastStatus)\$'
    # ...                                                                                   

Nó khá thuận tiện để quan sát cách một số chương trình tuân theo các quy ước mã thoát và một số không, để tìm hiểu về các quy ước mã thoát hoặc chỉ để có thể thấy những gì đang diễn ra dễ dàng hơn. Đã sử dụng nó một thời gian, tôi có thể nói rằng nhiều tập lệnh shell định hướng hệ thống thực hiện theo các quy ước. EX_USAGElà đặc biệt khá phổ biến, mặc dù các mã khác, không nhiều. Thỉnh thoảng tôi cố gắng tuân theo các quy ước, mặc dù luôn có $S_EX_ANY(1) cho những người lười biếng (tôi là một).


Tôi tự hỏi liệu có bất cứ điều gì như ánh xạ giữa mã errno và mã thoát để sử dụng nếu lỗi được báo cáo với mã errno đó dẫn đến thoát lỗi. Tôi có thể cần phải đưa ra một số ánh xạ hợp lý.
PSkocik

1
Ồ Tôi không mong đợi một câu trả lời công phu như vậy. Tôi chắc chắn sẽ thử nó như một cách tốt để xem các lệnh khác nhau hoạt động như thế nào. Cảm ơn.
Anthony G - công lý cho Monica

4

Miễn là bạn ghi lại mã thoát của mình để bạn nhớ chúng một năm kể từ bây giờ khi bạn phải quay lại và chỉnh sửa tập lệnh, bạn sẽ ổn. Ý tưởng về "mã thoát dành riêng" không thực sự được áp dụng nữa ngoài việc nói rằng thông thường sẽ sử dụng 0làm mã thành công và bất kỳ thứ gì khác làm mã lỗi.


4

Tài liệu tham khảo tốt nhất tôi có thể tìm thấy là đây: http://tldp.org/LDP/abs/html/exitcodes.html

Theo đó:

1 là một lỗi chung cho các lỗi và tôi luôn thấy nó được sử dụng cho các lỗi do người dùng xác định.

2 là để sử dụng sai shell shell, chẳng hạn như lỗi cú pháp

Để trả lời trực tiếp câu hỏi của bạn, tập lệnh của bạn sẽ ổn khi sử dụng các mã lỗi dành riêng, nó sẽ hoạt động như mong đợi, giả sử bạn xử lý lỗi dựa trên mã lỗi = 1/2/3.

Tuy nhiên, nó có thể gây nhầm lẫn nếu bạn gặp bất kỳ ai biết và sử dụng mã lỗi dành riêng, điều này có vẻ khá hiếm.

Một tùy chọn khác có sẵn cho bạn là lặp lại lỗi nếu có một lỗi và sau đó thoát, giả sử tập lệnh của bạn tuân theo quy ước Linux "không có tin nào là tin tốt" và không có gì thành công.

if [ $? -ne 0 ];then
    echo "Error type"
    exit 1
fi

2

Dựa trên các câu trả lời tôi đã nhận được (rất khó để chọn một câu hỏi khác), việc chỉ ra một số loại lỗi nhất định bằng cách sử dụng mã thoát mà Bash cũng sử dụng là không có hại . Bash (hoặc bất kỳ shell Unix nào khác) sẽ không làm gì đặc biệt (như chạy trình xử lý ngoại lệ) nếu tập lệnh người dùng thoát với một trong các mã lỗi này.

Có vẻ như tác giả của Hướng dẫn Bash-Scripting nâng cao đồng ý với các nỗ lực của BSD để chuẩn hóa mã thoát ( sysexits.h) và chỉ khuyến nghị rằng khi người dùng viết tập lệnh shell, họ không chỉ định mã thoát xung đột với mã thoát được xác định trước đang sử dụng, nghĩa là, họ giới hạn mã thoát tùy chỉnh của họ ở 50 mã trạng thái khả dụng trong phạm vi 64-113.

Tôi đánh giá cao ý tưởng (và lý do) nhưng tôi thích hơn nếu tác giả rõ ràng hơn rằng không có hại khi bỏ qua lời khuyên - ngoài các trường hợp người tiêu dùng tập lệnh đang kiểm tra các lỗi như ví dụ được trích dẫn của 127 ( command not found).

Thông số kỹ thuật POSIX có liên quan

Tôi đã nghiên cứu những gì POSIX nói về mã thoát và đặc tả POSIX dường như đồng ý với tác giả của Hướng dẫn Bash-Scripting nâng cao. Tôi đã trích dẫn các thông số kỹ thuật POSIX có liên quan (nhấn mạnh của tôi):

Trạng thái thoát cho lệnh

Mỗi lệnh có một trạng thái thoát có thể ảnh hưởng đến hành vi của các lệnh shell khác. Trạng thái thoát của các lệnh không phải là tiện ích được ghi lại trong phần này. Trạng thái thoát của các tiện ích tiêu chuẩn được ghi lại trong các phần tương ứng của chúng.

Nếu không tìm thấy lệnh, trạng thái thoát sẽ là 127. Nếu tìm thấy tên lệnh, nhưng nó không phải là tiện ích thực thi, trạng thái thoát sẽ là 126. Các ứng dụng gọi tiện ích mà không sử dụng shell nên sử dụng các giá trị trạng thái thoát này để báo cáo lỗi tương tự.

Nếu một lệnh thất bại trong quá trình mở rộng hoặc chuyển hướng từ, trạng thái thoát của nó sẽ lớn hơn 0.

Trong nội bộ, với mục đích quyết định xem một lệnh có thoát với trạng thái thoát khác không, shell sẽ nhận ra toàn bộ giá trị trạng thái được truy xuất cho lệnh bằng cách tương đương với hàm WEXITSTATUS của hàm Wait () (như được xác định trong khối lượng Giao diện hệ thống của POSIX.1-2008). Khi báo cáo trạng thái thoát với tham số đặc biệt '?', Shell sẽ báo cáo đầy đủ tám bit trạng thái thoát có sẵn. Trạng thái thoát của lệnh kết thúc vì nhận được tín hiệu sẽ được báo cáo là lớn hơn 128.

các exittiện ích

Như đã giải thích trong các phần khác, các giá trị trạng thái thoát nhất định đã được dành riêng cho các mục đích sử dụng đặc biệt và chỉ được sử dụng cho các ứng dụng cho các mục đích đó:

  • 126 - Một tập tin được thực thi đã được tìm thấy, nhưng nó không phải là một tiện ích thực thi.
  • 127 - Không tìm thấy tiện ích được thực thi.
  • >128 - Một lệnh bị gián đoạn bởi một tín hiệu.

Thêm thông tin

Để biết giá trị của nó, tôi đã có thể xác minh tất cả trừ một trong danh sách Mã thoát với ý nghĩa đặc biệt . Bảng mã thoát này rất hữu ích vì nó cung cấp thêm chi tiết - và ví dụ về cách tạo mã lỗi được ghi lại trong tham chiếu Bash .

Cố gắng tạo trạng thái thoát 128

Sử dụng phiên bản Bash 3.2.25 và 4.2.46, tôi đã cố gắng đưa 128 Invalid argument to exitra lỗi nhưng mỗi lần tôi nhận được 255 (Thoát trạng thái ra khỏi phạm vi). Ví dụ: nếu exit 3.14159được thực thi như một phần của tập lệnh shell hoặc trong trình bao con tương tác, trình bao thoát ra với mã 255:

$ exit 3.14159
exit
bash: exit: 3.14159: numeric argument required

Để vui hơn nữa, tôi cũng đã thử chạy một chương trình C đơn giản nhưng trong trường hợp này, có vẻ như exit(3)hàm này chỉ đơn giản chuyển đổi float thành int (3 trong trường hợp này) trước khi thoát:

#include <stdlib.h>
main()
{
    exit(3.14159);
}
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.