쉘 스크립트의 종료 상태에 “예약 된”코드 사용 코드를 알지 못했기 때문에 첫 번째 오류

최근 에 고급 Bash 스크립팅 안내서 에서이 특수한 의미가진 종료 코드 목록을 발견했습니다 . 이들은이 코드를 예약 된 것으로 참조하고 다음을 권장합니다.

위의 표에 따르면, 종료 코드 1-2, 126-165 및 255는 특별한 의미를 가지므로 사용자 지정 종료 매개 변수에 대해서는 피해야합니다.

얼마 전에 다음과 같은 종료 상태 코드를 사용하는 스크립트를 작성했습니다.

  • 0-성공
  • 1-잘못된 호스트 이름
  • 2-잘못된 인수가 지정되었습니다
  • 3-불충분 한 사용자 권한

스크립트를 작성할 때 특수 종료 코드를 알지 못했기 때문에 첫 번째 오류 조건에 대해 1에서 시작하여 각 연속 오류 유형에 대해 종료 상태를 증가 시켰습니다.

나는 나중 단계에서 다른 스크립트 (0이 아닌 종료 코드를 확인할 수 있음)에 의해 호출 될 수 있도록 스크립트를 작성했습니다. 나는 실제로 그것을 아직하지 않았다. 지금까지 대화 형 셸 (Bash)에서만 스크립트를 실행했으며 사용자 정의 종료 코드를 사용하여 어떤 문제가 발생할 수 있는지 궁금합니다. Advanced Bash-Scripting Guide의 권장 사항은 어느 정도 중요합니까?

Bash 문서에서 확실한 조언을 찾을 수 없었습니다. 종료 상태 섹션 에는 Bash가 사용하는 종료 코드가 나열되어 있지만 이들 중 어느 것도 자신의 스크립트 / 프로그램에 사용하도록 예약 또는 경고 되어 있지는 않습니다 .



답변

프로세스 종료 코드의 의미를 표준화하려는 여러 시도가있었습니다. 언급 한 것 외에도 다음을 알고 있습니다.

  • BSD는 sysexits.h64 이상의 값에 대한 의미를 정의합니다.

  • grep코드 0을 종료하는 GNU 문서는 하나 이상의 일치 항목이 발견되었고 1은 일치 항목이 없음을 의미하고 2는 I / O 오류가 발생했음을 의미합니다. 이 규칙은 “아무것도 없었지만 아무것도 찾지 못했습니다”와 “I / O 오류가 발생했습니다”라는 의미가있는 다른 프로그램에도 유용합니다.

  • C 라이브러리 함수의 많은 구현은 system종료 코드 127을 사용하여 프로그램이 존재하지 않거나 시작하지 못했음을 나타냅니다.

  • Windows에서는 NTSTATUS코드 (32 비트 숫자 공간 전체에 불편하게 흩어져 있음)가 종료 코드로 사용될 수 있으며, 특히 심각한 오작동 (예 :)으로 인해 프로세스가 종료되었음을 나타내는 코드가 종료 코드로 사용될 수 있습니다 STATUS_STACK_OVERFLOW.

이러한 규칙 중 하나를 준수하는 특정 프로그램을 신뢰할 수 없습니다. 신뢰할 수있는 유일한 규칙은 종료 코드 0이 성공이고 다른 것은 실패라는 것입니다. (C89의 값이 0 인 EXIT_SUCCESS것은 아닙니다 . 그러나 값이 같지 않아도 exit(0)동일하게 동작해야합니다 exit(EXIT_SUCCESS).)


답변

없음 종료 코드는 특별한 의미가 없습니다 만,의 값은 $?특별한 의미를 가질 수있다.

Bourne Shell 및 ksh93이 종료 코드 및 오류 상황을 처리하고 쉘 변수 $?에 전달하는 방식이 문제입니다. 나열한 내용과 달리 다음 값만 $?특별한 의미를 갖습니다.

  • 126 바이너리가 존재하더라도 실행할 수 없습니다
  • 127 지정된 바이너리가 존재하지 않습니다
  • 128 종료 상태는 == 0이지만 지정되지 않은 문제가 있습니다.

또한 $?신호에 의해 중단 된 프로그램을 위해 예약 된 지정되지 않은 쉘 및 플랫폼 별 코드 범위 > 128이 있습니다.

  • Bourne Shell bash 및 ksh88은 128 + 신호 번호를 사용합니다.
  • ksh93은 256 + 신호 번호를 사용합니다.

다른 값은 쉘 특수 $?값 과 구별 될 수 있으므로 문제를 일으키지 않습니다 .

특히, 값 1과 2는 특수 조건에 사용되지 않고 내장 명령이 없을 때 동일하게 작동 할 수있는 내장 명령에 의해 사용되는 종료 코드 일뿐입니다. 따라서 제공 한 bash 스크립팅 안내서에 대한 포인터 는 특정 코드가 자체 스크립트에서 피해야하는 특수 값인지 여부를 언급하지 않고 bash에서 사용하는 코드를 나열하기 때문에 좋은 매뉴얼이 아닙니다.

Bourne Shell의 최신 버전은 프로그램이 종료 될 때까지 기다리지 waitid()않고 SVr4에 대해 1989 년에 도입되어 더 나은 syscall 인터페이스를 사용합니다 (1980 년 UNOS에서 이미 사용한 것과 유사).waitpid()waitid()

새로운 Bourne 쉘 버전은 인코딩으로 종료 이유 별도의 변수에 ${.sh.code}/ ${.sh.codename}종료 코드${.sh.status}/ ${.sh.termsig}, 참조 http://schillix.sourceforge.net/man/man1/bosh.1.html , 종료 코드에 과부하가되지 않습니다 특수 상태와`waitid () 사용의 결과로 Bourne Shell은 이제 하위 8 비트뿐만 아니라 종료 코드의 모든 32 비트 반환을 지원합니다.

BTW : exit(256)C 프로그램 또는 쉘 스크립트와 유사하거나 유사 하지 않도록주의하십시오 $?. 클래식 쉘에서는 0으로 해석됩니다.


답변

쉘 스크립팅의 경우, 때때로 이름이 sysexist.h붙은 쉘 예약 종료 코드 (접두사)와 동등한 쉘을 소스로 제공합니다.S_EX_exit.sh

기본적으로 다음과 같습니다.

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
#...

다음을 사용하여 생성 할 수 있습니다.

#!/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; }'

그래도 많이 사용하지는 않지만 오류 코드를 문자열 형식으로 뒤집는 셸 함수가 사용됩니다. 나는 그것을 명명했다 exit2str. 위의 exit.shgenerator 이름을 지정했다고 가정하면 ( )로 exit.sh.sh코드를 exit2str생성 할 수 있습니다 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
}"

PS1대화 형 셸 에서 이것을 사용하여 각 명령을 실행 한 후 종료 상태와 문자열 형식을 알 수 있습니다 (알려진 문자열 형식이있는 경우).

[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)$

이를 얻으려면 exit2str 함수에 대한 정보가 필요하지 않습니다.

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

그런 다음이를 사용 ~/.bashrc하여 각 명령 프롬프트에서 종료 코드를 저장 및 번역하고 프롬프트 ( PS1)를 표시하십시오 .

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

일부 프로그램이 종료 코드 규칙을 따르는 방법을 관찰하고 일부 프로그램은 종료 코드 규칙에 대해 배우거나 더 쉽게 진행되는 것을 볼 수 있도록하는 데 매우 유용합니다. 한동안 사용해 본 결과, 많은 시스템 지향 쉘 스크립트가 규칙을 따릅니다. EX_USAGE다른 코드는 많지 않지만 특히 일반적입니다. $S_EX_ANY게으른 사람들을 위해 항상 (1) 있지만 (나는 하나입니다) 나는 때때로 규칙을 따르려고 노력합니다 .


답변

종료 코드를 문서화하여 돌아와서 스크립트를 조정해야 할 때부터 1 년 후를 기억하는 한 괜찮습니다. “예약 된 종료 코드”라는 아이디어는 0성공 코드 및 실패 코드로 사용 하는 것이 일반적이라고 말하는 것 외에는 더 이상 적용되지 않습니다 .


답변

내가 찾을 수있는 가장 좋은 참조는 다음과 같습니다 : http://tldp.org/LDP/abs/html/exitcodes.html

이것에 따르면 :

1 오류에 대한 일반적인 포괄이며 항상 사용자 정의 오류에 사용되는 것을 보았습니다.

2 구문 오류와 같은 쉘 내장을 잘못 사용하기위한 것

귀하의 질문에 직접 대답하기 위해 예약 된 오류 코드를 사용하면 스크립트가 정상적으로 작동합니다. 오류 코드 = 1/2/3에 따라 오류를 처리한다고 가정하면 예상대로 작동합니다.

그러나 예약 된 오류 코드를 알고 사용하는 사람이 있으면 혼동 될 수 있습니다.

스크립트가 “뉴스가 좋지 않다”라는 Linux 규칙을 따르고 성공에 에코가 없다고 가정 할 때 오류가있는 경우 오류를 에코 한 다음 종료하는 것도 가능합니다.

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

답변

내가받은 답변 (다른 것들 중 하나를 선택하기가 어려웠습니다)에 따라 Bash가 사용하는 종료 코드를 사용하여 특정 유형의 오류를 나타내는 것은 해롭지 않습니다 . 사용자 스크립트가 이러한 오류 코드 중 하나로 종료되면 Bash (또는 다른 Unix 쉘)는 예외 처리기 실행과 같은 특별한 작업을 수행하지 않습니다.

고급 Bash 스크립팅 가이드의 저자가 종료 코드 (표준화하기 위해 BSD 시도에 동의 것으로 보인다 sysexits.h)과 단순히 추천 사용자가 쉘 스크립트를 작성할 때, 그들은 종료 코드를 지정하지 않는 것이 이미 사전 정의 된 종료 코드와 충돌 즉, 사용자 정의 종료 코드가 64-113 범위의 사용 가능한 50 개의 상태 코드로 제한됩니다.

아이디어 (및 이론적 근거)에 감사하지만 저자가 127의 인용 된 예와 같은 오류를 검사하는 경우를 제외하고는 조언을 무시하는 것이 해롭지 않다는 것이 더 명백한 경우 선호합니다. ( command not found).

관련 POSIX 사양

종료 코드에 대한 POSIX의 의견을 조사했으며 POSIX 사양은 Advanced Bash-Scripting Guide의 저자와 일치하는 것으로 보입니다. 관련 POSIX 사양 (강조 광산)을 인용했습니다.

명령 종료 상태

각 명령에는 다른 쉘 명령의 작동에 영향을 줄 수있는 종료 상태가 있습니다. 유틸리티가 아닌 명령의 종료 상태는이 섹션에 설명되어 있습니다. 표준 유틸리티의 종료 상태는 해당 섹션에 설명되어 있습니다.

명령을 찾을 수 없으면 종료 상태는 127입니다. 명령 이름을 찾았지만 실행 가능한 유틸리티가 아닌 경우 종료 상태는 126입니다. 쉘을 사용하지 않고 유틸리티를 호출하는 응용 프로그램은이 종료 상태 값을 사용해야합니다. 비슷한 오류를보고합니다.

단어 확장 또는 리디렉션 중에 명령이 실패하면 종료 상태는 0보다 커야합니다.

내부적으로 명령이 0이 아닌 종료 상태로 종료되는지 여부를 결정하기 위해 쉘은 시스템 인터페이스 볼륨에 정의 된 wait () 함수 WEXITSTATUS 매크로에 해당하는 명령에 대해 검색된 전체 상태 값을 인식해야합니다. POSIX.1-2008). 특수 매개 변수 ‘?’를 사용하여 종료 상태를보고 할 때 쉘은 사용 가능한 전체 8 비트의 종료 상태를보고해야합니다. 신호를 수신하여 종료 된 명령의 종료 상태는 128보다 큰 것으로보고됩니다.

exit유틸리티

다른 섹션에서 설명했듯이 특정 종료 상태 값은 특수 용도 로 예약되어 있으며 해당 용도로만 응용 프로그램에서 사용해야 합니다.

  • 126 – 실행할 파일을 찾았지만 실행 파일이 아닙니다.
  • 127 – 실행할 유틸리티를 찾을 수 없습니다.
  • >128 – 신호에 의해 명령이 중단되었습니다.

추가 정보

가치있는 것을 위해, 나는 특별한 의미를 가진 종료 코드 목록 중 하나를 제외한 모든 것을 확인할 수있었습니다 . 이 종료 코드 테이블은 Bash 참조에 설명 된 오류 코드 생성 방법에 대한 자세한 정보 및 예제를 제공하므로 유용합니다 .

128의 종료 상태 생성 시도

Bash 버전 3.2.25 및 4.2.46을 사용하여 128 Invalid argument to exit오류를 발생 시켰지만 255 (종료 상태가 범위를 벗어남)를 수신 할 때마다. 예를 들어, exit 3.14159쉘 스크립트 또는 대화식 자식 쉘의 일부로 실행되면 쉘은 255다음 코드로 종료됩니다 .

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

더 재미있게, 나는 또한 간단한 C 프로그램을 실행하려고 시도했지만 exit(3)이 경우 종료하기 전에 함수가 float를 int (이 경우 3)로 간단히 변환 한 것처럼 보입니다 .

#include <stdlib.h>
main()
{
    exit(3.14159);
}