Bash 스크립트에서 특정 조건이 발생하면 어떻게 전체 스크립트를 종료 할 수 있습니까?


717

Bash에서 스크립트를 작성하여 일부 코드를 테스트하고 있습니다. 그러나 코드 컴파일이 처음에 실패하면 테스트를 실행하는 것이 어리석은 것처럼 보입니다.이 경우 테스트를 중단합니다.

전체 스크립트를 while 루프 안에 래핑하지 않고 나누기를 사용하지 않고도이 작업을 수행 할 수 있습니까? 던 던 던 고토 같은 것 ?

답변:


809

이 진술을 시도하십시오 :

exit 1

1적절한 오류 코드로 교체하십시오 . 특별한 의미의 종료 코드를 참조하십시오 .


4
@CMCDragonkai, 일반적으로 0이 아닌 코드가 작동합니다. 특별한 것이 필요하지 않으면 1일관되게 사용할 수 있습니다 . 스크립트가 다른 스크립트에 의해 실행되는 경우 특정 의미로 고유 한 상태 코드 세트를 정의 할 수 있습니다. 예를 들어 1== 테스트 실패, 2== 컴파일 실패 스크립트가 다른 것의 일부인 경우, 스크립트가 사용 된 실습과 일치하도록 코드를 조정해야 할 수도 있습니다. 예를 들어 automake가 테스트 스위트의 일부를 실행 77하면 테스트를 건너 뛴 것으로 표시하는 데 코드 가 사용됩니다.
Michał Górny

21
아니 이것도 창을 닫습니다, 그냥 스크립트를 종료하지 않습니다
Toni Leigh

7
@ToniLeigh bash에는 "창"개념이 없으므로 특정 설정 (예 : 터미널 에뮬레이터)과 관련하여 혼동 될 수 있습니다.
Michael Foukarakis 2016 년

3
@ToniLeigh "창"을 닫으면 exit #스크립트가 아닌 함수 안에 명령을 넣을 가능성이 높습니다 . (이 경우에 return #대신 사용하십시오.)
Jamie

1
그것이 성공 0 수단 @Sevenearths, 따라서 exit 00 (이 성공이 스크립트의 결과를 사용할 수있는 다른 스크립트 이야기) 스크립트 반환을 종료
VictorGalisson

689

set -e 사용

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

스크립트는 실패한 첫 번째 줄 이후에 종료됩니다 (0이 아닌 종료 코드를 반환 함). 이 경우 command-that-fails2 가 실행되지 않습니다.

모든 단일 명령의 반환 상태를 확인하면 스크립트는 다음과 같습니다.

#!/bin/bash

# I'm assuming you're using make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

set -e를 사용하면 다음과 같습니다.

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

실패한 명령은 전체 스크립트가 실패하고 $?로 확인할 수있는 종료 상태를 반환합니다 . . 스크립트가 너무 길거나 많은 것들을 만들고 있다면 어디에서나 리턴 상태 확인을 추가하면 꽤 추악해질 것입니다.


10
으로 set -e당신은 아직 할 수 있도록 몇 가지 스크립트를 중지하지 않고 오류가있는 명령 출구 : command 2>&1 || echo $?.
Adobe

6
set -e파이프 라인 또는 명령 구조가 0이 아닌 값을 반환하면 스크립트를 중단합니다. 예를 들어 foo || bar경우에만 모두 실패 foo하고 bar아닌 값을 반환합니다. 일반적으로 잘 작성된 bash 스크립트는 set -e시작할 때 추가하고 추가는 자동 온 전성 검사 기능으로 작동합니다. 문제가 있으면 스크립트를 중단하십시오.
Mikko Rantalainen

6
명령을 함께 파이핑하는 경우 set -o pipefail옵션 을 설정하여 명령이 실패하면 실패 할 수도 있습니다.
Jake Biesinger

18
실제로 관용구가없는 관습 set -e은 단지 것 make || exit $?입니다.
tripleee

4
또한 있습니다 set -u. 받는 살펴보세요 비공식 bash는 엄격 모드 : set -euo pipefail.
Pablo A

236

한 SysOps 사람이 나에게 Three-Fingered Claw 기술을 가르쳐주었습니다.

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }

이 기능은 * NIX OS 및 쉘 풍미에 강합니다. 스크립트 시작 부분 (bash 또는 기타)에 배치하십시오.try() 문장과 코드를 넣으십시오.

설명

( 비행 양 댓글을 기반으로 함 ).

  • yell: 스크립트 이름과 모든 인수를 다음에 인쇄하십시오 stderr.
    • $0 스크립트의 경로입니다.
    • $* 모든 주장입니다.
    • >&2>stdout을 & pipe로 리디렉션2 하는 것을 의미합니다 . 파이프1stdout 자체 가 될 것입니다.
  • die와 동일 yell하지만 0아닌 종료 상태로 종료 됩니다. 이는 "실패"를 의미합니다.
  • try||(boolean OR)을 사용하는데 , 왼쪽이 실패한 경우에만 오른쪽을 평가합니다.

3
유닉스 스크립팅을 처음 접했습니다. 위의 기능이 어떻게 실행되는지 설명해 주시겠습니까? 익숙하지 않은 새로운 구문이 표시됩니다. 감사.
kaizenCoder

15
yell : $0스크립트의 경로입니다. $*모든 주장입니다. >&2>stdout을 &파이프로 리디렉션 ”을 의미합니다 2. 파이프 1은 stdout 자체입니다. 따라서 yell은 모든 인수 앞에 스크립트 이름을 붙이고 stderr에 인쇄합니다. dieyell 과 동일 하지만 0이 아닌 종료 상태로 종료됩니다. 이는 "실패"를 의미합니다. 시도 사용을 부울 또는 ||왼쪽 중 하나가 실패하지 않은 경우에만 오른쪽을 평가. $@다시 모든 인수이지만 다릅니다 . 모든 것을 설명하는 희망
날아 다니는 양

1
die() { yell "$1"; exit $2; }메시지를 전달하고 종료 코드를 사용할 수 있도록 이것을 수정했습니다.die "divide by zero" 115 .
Mark Lakata

3
내가 사용하는 거라고 방법을 볼 수 있습니다 yelldie. 그러나 try그렇게 많지는 않습니다. 당신이 그것을 사용하는 예를 제공 할 수 있습니까?
kshenoy

2
흠,하지만 스크립트에서 어떻게 사용합니까? "try () 문과 코드를 켜십시오"라는 의미를 이해하지 못합니다.
TheJavaGuy-Ivan Milosavljević

33

로 스크립트를 호출하는 경우 스크립트 종료 상태가 될 위치 를 source사용할 수 있습니다 (오류 또는 false에 0이 아닌 값 사용). 그러나 실행 가능한 스크립트를 호출하면 (예 : 파일 이름으로 직접) return 문에 문제가 발생합니다 ( "return : 함수 또는 소스 스크립트에서`return '만 할 수 있습니다"라는 오류 메시지).return <x><x>

경우 exit <x>대신 사용되는 스크립트가 호출 될 때 source,이 스크립트를 시작 쉘을 종료 될 것이다, 그러나 예상대로 실행 스크립트는 그냥 종료됩니다.

동일한 스크립트에서 두 경우 모두를 처리하기 위해

return <x> 2> /dev/null || exit <x>

이것은 어떤 호출이 적합한 지 처리합니다. 스크립트의 최상위 레벨에서이 문장을 사용한다고 가정합니다. 함수 내에서 스크립트를 직접 종료하지 않는 것이 좋습니다.

참고 : <x>숫자 일뿐입니다.


함수 내에서 return / exit가있는 스크립트에서 나를 위해 작동하지 않습니다. ?
1 월

@jan 호출자가 반환 값으로 수행하거나 수행하지 않는 것은 함수에서 반환하는 방법과 완전히 직교 적입니다 (즉, 호출에 상관없이 쉘을 종료하지 않음). 이는 주로 이러한 Q & A에 속하지 않은 발신자 코드에 의존한다. 함수의 리턴 값을 호출자의 요구에 맞게 조정할 수도 있지만이 응답 이 리턴 값이 제한 될 수 없습니다.
kavadias

11

나는 종종 오류를 처리하기 위해 run ()이라는 함수를 포함합니다. 내가 만들고자하는 모든 호출은이 함수로 전달되므로 오류가 발생하면 전체 스크립트가 종료됩니다. set -e 솔루션에 비해이 방법의 장점은 줄이 실패 할 때 스크립트가 자동으로 종료되지 않고 문제가 무엇인지 알려줄 수 있다는 것입니다. 다음 예제에서는 스크립트가 false를 호출 할 때 종료되므로 3 번째 행이 실행되지 않습니다.

function run() {
  cmd_output=$(eval $1)
  return_value=$?
  if [ $return_value != 0 ]; then
    echo "Command $1 failed"
    exit -1
  else
    echo "output: $cmd_output"
    echo "Command succeeded."
  fi
  return $return_value
}
run "date"
run "false"
run "date"

1
어떤 이유로 든, 나는이 답변을 정말로 좋아합니다. 좀 더 복잡하다는 것을 알고 있지만 매우 유용합니다. 그리고 나는 배쉬 전문가가 아니기 때문에 내 논리가 잘못되었다고 믿게 되고이 방법론에 문제가 있다고 생각합니다. 그렇지 않으면 다른 사람들이 더 칭찬을했을 것이라고 생각합니다. 그렇다면이 기능의 문제점은 무엇입니까? 여기서 찾아야 할 것이 있습니까?

eval을 사용하는 이유를 기억하지 못합니다.이 함수는 cmd_output = $ ($ 1)에서 잘 작동합니다.
Joseph Sheedy

방금 복잡한 배포 프로세스의 일부로 이것을 구현했으며 환상적으로 작동했습니다. 감사합니다. 여기에 의견과 공감대가 있습니다.
fuzzygroup 2016 년

정말 놀라운 일입니다! 이것은 가장 효과적이고 가장 간단한 솔루션입니다. FOR 루프는를 선택하지 않으므로 FOR 루프의 명령 앞에 이것을 추가했습니다 set -e option. 그런 다음 명령에 인수가 포함되어 있으므로 괄호 문제를 피하기 위해 작은 따옴표를 사용했습니다 runTry 'mysqldump $DB_PASS --user="$DB_USER" --host="$BV_DB_HOST" --triggers --routines --events --single-transaction --verbose $DB_SCHEMA $tables -r $BACKUP_DIR/$tables$BACKUP_FILE_NAME'. 참고 함수의 이름을 runTry로 변경했습니다.
Tony-Caffe

1
eval임의의 입력을 받아들이면 잠재적으로 위험하지만 그렇지 않으면 꽤 좋습니다.
dragon788

5

구성 대신 단락 평가를if 활용할 수 있습니다 .

#!/usr/bin/env bash

echo $[1+1]
echo $[2/0]              # division by 0 but execution of script proceeds
echo $[3+1]
(echo $[4/0]) || exit $? # script halted with code 1 returned from `echo`
echo $[5+1]

대체 연산자의 우선 순위로 인해 필요한 괄호 쌍에 유의하십시오. $?가장 최근에 호출 된 명령의 코드를 종료하도록 설정된 특수 변수입니다.


1
내가 할 경우 command -that --fails || exit $?괄호없이 작동에 대해 그것을 무엇 echo $[4/0]우리가 그들을 필요로하는 그 원인은?
Anentropic

2
@Anentropic @skalee 괄호는 우선 순위와 관련이 있지만 예외 처리와는 관련이 없습니다. 으로 나누기는 괄호 (즉, 간단한없이 코드 1. 쉘에서 즉시 종료의 원인이됩니다 echo $[4/0] || exit $?) bash는 을 실행하지 않습니다가 echo, 혼자 순종하자 ||.
bobbogo

1

나는 같은 질문을 가지고 있지만 중복 될 수 있기 때문에 요청할 수 없습니다.

스크립트가 조금 더 복잡하면 exit를 사용하여 허용 된 답변이 작동하지 않습니다. 백그라운드 프로세스를 사용하여 조건을 확인하는 경우 exit는 하위 셸에서 실행될 때만 해당 프로세스를 종료합니다. 스크립트를 죽이려면 명시 적으로 죽여야합니다 (적어도 내가 아는 유일한 방법입니다).

수행 방법에 대한 간단한 스크립트는 다음과 같습니다.

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # does not work
        ((N > 5)) && { kill -9 $$; exit 5; } # works 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

이것은 더 나은 대답이지만 여전히 불완전합니다 . 부분을 제거하는 방법을 모르겠습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.