프로세스 종료 코드를 기반으로 종료 쉘 스크립트


378

많은 명령을 실행하는 쉘 스크립트가 있습니다. 명령이 0이 아닌 종료 코드로 종료되면 쉘 스크립트를 종료하려면 어떻게합니까?


3
bash를 사용한다고 가정했지만 대답이 매우 다른 경우 게시물에 지정할 수 있습니까?
Martin W

어려운 방법 : $?모든 명령 후의 값을 테스트하십시오 . 쉬운 방법 : 넣어 set -e또는#!/bin/bash -e 배시 스크립트의 상단에.
mwfearnley

답변:


494

각 명령 후에는 종료 코드를 $?변수 에서 찾을 수 있으므로 다음과 같이됩니다.

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

$?코드에서 파이프의 마지막 요소에 대한 리턴 코드 만 제공 하므로 파이프 된 명령에주의해야합니다 .

ls -al file.ext | sed 's/^/xx: /"

파일이 존재하지 않으면 오류 코드를 반환하지 않습니다. sed 파이프 라인 일부가 실제로 작동하기 때문에 0을 반환 함).

bash쉘 실제로,이 경우에 도움이 될 수있는 배열을 제공되는 것 PIPESTATUS. 이 배열에는 각 파이프 라인 구성 요소마다 하나의 요소가 있으며 ${PIPESTATUS[0]}다음 과 같이 개별적으로 액세스 할 수 있습니다 .

pax> false | true ; echo ${PIPESTATUS[0]}
1

이것은 false전체 파이프 라인이 아니라 명령 의 결과를 가져옵니다 . 적합하다고 생각되면 전체 목록을 처리 할 수도 있습니다.

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

파이프 라인에서 가장 큰 오류 코드를 얻으려면 다음과 같이 사용할 수 있습니다.

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

이것은 각 PIPESTATUS요소를 차례로 통과 rc하여 이전 rc값 보다 큰 경우 저장합니다 .


39
한 줄의 휴대용 코드에서 동일한 기능 : ls -al file.ext || exit $?([[]]는 이식 할 수 없습니다.)
MarcH

19
월, 나는 당신이 찾을 것 같아 [[ ]]매우 휴대용에 bash질문 :-) 이상하게 태그가 무엇인가, ls에 일을하지 않는 command.com그렇지 휴대용, 그럴듯한 I의 노하우 중 하나 그래서하지만 인수의 같은 종류의 당신 선물.
paxdiablo

39
나는이 고대 알고, 그러나 당신이 배열을 통해 파이프 명령의 종료 코드를 얻을 수 있다는 것을 주목해야한다 PIPESTATUS, 즉 ( ${PIPESTATUS[0]}첫 번째 명령 ${PIPESTATUS[1]}또는 두 번째에 대한 ${PIPESTATUS[*]}모든 출구 stati의 목록.
DevSolar

11
우아하고 관용적 인 쉘 스크립팅은 $?직접 조사 할 필요가 거의 없다는 점을 강조해야 합니다. 일반적으로 if ls -al file.ext; then : nothing; else exit $?; fi@MarcH와 같은 내용이 필요 ls -al file.ext || exit $?하지만 thenor else절이 다소 복잡하면 유지 관리가 더 쉽습니다 .
tripleee

9
[[ $rc != 0 ]]당신에게 줄 것이다 0: not found또는 1: not found오류가 발생했습니다. 로 변경해야합니다 [ $rc -ne 0 ]. 또한 rc=$?제거하고 방금 사용할 수 [ $? -ne 0 ]있습니다.
CurtisLeeBolin

223

$?로 작업하려면 $?부터 각 명령 후에 확인해야합니다. 각 명령이 종료 된 후 업데이트됩니다. 즉, 파이프 라인을 실행하면 파이프 라인에서 마지막 프로세스의 종료 코드 만 가져옵니다.

다른 접근 방법은 다음과 같습니다.

set -e
set -o pipefail

이것을 쉘 스크립트의 맨 위에두면 bash가 이것을 처리하는 것처럼 보입니다. 이전 포스터에서 언급했듯이 "set -e"는 간단한 명령에서 bash가 오류와 함께 종료되도록합니다. "set -o pipefail"은 파이프 라인의 모든 명령에서 bash가 오류와 함께 종료되도록합니다.

이 문제에 대한 자세한 내용은 여기 또는 여기 를 참조 하십시오 . 여기 에 내장 된 bash 매뉴얼 섹션이 있습니다.


6
이것은 실제로 가장 좋은 대답이어야합니다 PIPESTATUS. 종료 코드를 어디서나 사용 하고 확인 하는 것보다 훨씬 쉽습니다 .
candu

2
#!/bin/bash -e쉘 스크립트를 시작하는 유일한 방법입니다. foo || handle_error $?실제로 종료 상태를 조사해야하는 경우 와 같은 것을 항상 사용할 수 있습니다 .
Davis Herring

53

" set -e"이 가장 쉬운 방법 일 것입니다. 프로그램의 명령 앞에 넣으십시오.


6
@SwaroopCH set -e스크립트의 명령이 오류 상태로 종료 되고이 오류를 처리하지 않으면 스크립트가 중단됩니다.
Andrew

2
set -eset -o errexit전자는 검색 할 수없는 것과 100 % 동일합니다 . 공식 문서를 보려면 opengroup + errexit를 검색하십시오.
MarcH

30

매개 변수없이 bash에서 exit를 호출하면 마지막 명령의 종료 코드가 반환됩니다. OR과 결합하여 bash는 이전 명령이 실패한 경우에만 exit를 호출해야합니다. 그러나 나는 이것을 테스트하지 않았습니다.

command1 || 출구;
command2 || 출구;

Bash는 또한 마지막 명령의 종료 코드를 변수 $?에 저장합니다.



21

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. 어떻게의 종료 코드를받을 수 있나요 cmd1인을cmd1|cmd2

    첫째, cmd1종료 코드는 0이 아닐 수 있으며 여전히 오류를 의미하지는 않습니다. 예를 들어

    cmd | head -1

    종료 상태가 141 (또는 ksh93 인 269) 인 경우 SIGPIPE 신호에 의해 중단 cmd1되었기 때문입니다.cmdhead -1 한 줄을 읽은 후 종료 입니다.

    파이프 라인 요소의 종료 상태를 알려면 cmd1 | cmd2 | cmd3

    ㅏ. zsh로 :

    종료 코드는 pipestatus 특수 배열에 제공됩니다. cmd1종료 코드는 $pipestatus[1]이고 cmd3종료 코드는 $pipestatus[3]이므로 $?항상와 같습니다 $pipestatus[-1].

    비. bash와 함께 :

    종료 코드는 PIPESTATUS특수 배열 로 제공됩니다 . cmd1종료 코드는 ${PIPESTATUS[0]}이고 cmd3종료 코드는 ${PIPESTATUS[2]}이므로 $?항상와 같습니다 ${PIPESTATUS: -1}.

    ...

    자세한 내용은 다음 링크를 참조하십시오 .


19

bash의 경우 :

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}

19
성공을 나타내는 "0을 종료"해서는 안됩니다.
nobar

3
exit_code = $ ?; echo "오류로 인해 스크립트가 중단되었습니다"; exit $ exit_code
RaSergiy

1
@ HAL9001이 증거가 있습니까? IBM 문서 는 그렇지 않다고 말합니다 .
Patrick James McDougle

11

bash에서 이것은 쉽습니다. &&와 함께 묶으십시오.

command1 && command2 && command3

중첩 된 if 구문을 사용할 수도 있습니다.

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi

+1 이것은 내가 찾던 가장 간단한 솔루션이었습니다. 또한 if (! command)명령에서 0이 아닌 오류 코드가 예상되는 경우 작성할 수도 있습니다 .
Berci

이것은 sequencial 명령을위한 것입니다.이 3 개를 병렬로 시작하고 그중 하나라도 실패하면 모두를 죽이려면 어떻게해야합니까?
Vasile Surdu

4
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit

2
사용할 이유가 거의 없습니다 $*. "$@"공백과 와일드 카드를 보존 하려면 대신 사용 하십시오.
Davis Herring
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.