bash 스크립트에서 set -e는 무엇을 의미합니까?


713

패키지가 데비안 아카이브 (.deb) 파일에서 압축 해제되기 전에 스크립트가 실행하는 이 preinst 파일 의 내용을 연구하고 있습니다 .

스크립트에는 다음 코드가 있습니다.

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

첫 번째 질문은 다음과 같습니다.

set -e

스크립트의 나머지 부분은 매우 간단하다고 생각합니다. 데비안 / 우분투 패키지 관리자가 설치 작업을 실행하고 있는지 확인합니다. 있는 경우 내 응용 프로그램이 시스템에 설치되었는지 확인합니다. 있는 경우 스크립트는 "MyApplicationName이 설치되었습니다"라는 메시지를 인쇄 하고 종료됩니다 ( return 1"오류"로 끝나지 않습니까?).

사용자가 데비안 / 우분투 패키지 시스템에 패키지 설치를 요청하면 스크립트는 두 디렉토리도 삭제합니다.

이것이 맞습니까? 아니면 뭔가 빠졌습니까?



46
Google에서 이것을 찾을 수없는 이유 : 쿼리의 -e는 부정으로 해석됩니다. 다음 쿼리를 시도하십시오 : bash set "-e"
Maleev

3
나 자신에게 같은 질문을 한 때, 나는보고 있었다 @twalbergman set
Sedat Kilinc

4
끄는 방법을 찾고 있다면 대시를 더하기 접두사로 바꾸십시오.set +e
Tom Saleeba

@twalberg이지만 실제 사람들에게 묻는 것은 로봇에서 요청하는 것보다 훨씬 흥미 롭습니다. ;-).
vdegenne

답변:


797

보낸 사람 help set:

  -e  Exit immediately if a command exits with a non-zero status.

그러나 일부에서는 나쁜 습관으로 간주됩니다 (bash FAQ 및 irc freenode #bash FAQ 작성자). 다음을 사용하는 것이 좋습니다.

trap 'do_something' ERR

do_something오류 발생시 기능 을 실행 합니다.

http://mywiki.wooledge.org/BashFAQ/105 참조


14
"명령이 0이 아닌 상태로 종료되면 즉시 종료"와 동일한 의미를 원한다면 do_something은 무엇입니까?
CMCDragonkai

71
trap 'exit' ERR
chepner

12
그만큼 ERR트랩은 사용자가 기능을 가지고, 만약 그렇다면, 쉘 함수에 의해 상속되지 않습니다 set -o errtrace또는 set -E한 번만 트랩을 설정할 수 있도록 전 세계적으로 적용됩니다.
ykay

31
않습니다 trap 'exit' ERR아무것도 에서 다른를 set -e?
Andy

22
나쁜 습관이라면 왜 데비안 패키지에 사용 됩니까?
phuclv

98

set -e명령 또는 파이프 라인에 오류가있는 경우 스크립트 실행을 중지합니다. 이는 스크립트의 오류를 무시하는 기본 쉘 동작과 반대입니다. help set이 내장 명령에 대한 설명서를 보려면 터미널을 입력 하십시오.


44
파이프 라인 의 마지막 명령에 오류가있는 경우에만 실행을 중지 합니다. set -o pipefail선행 명령 중 하나가 0이 아닌 상태로 종료 된 경우 파이프 라인 명령의 리턴 값이 0이 아닌 오류를 전파하는 데 사용할 수 있는 Bash 특정 옵션 이 있습니다.
Anthony Geoghegan

2
파이프 라인의 0이 아닌 첫 번째 (즉, 오류가 있음 ) 명령의 종료 상태-o pipefail종료 됨을 의미합니다 -o errexit. 파이프 라인의 나머지 명령은 여전히을 사용하여 실행 됩니다 set -o errexit. 예를 들어 echo success | cat - <(echo piping); echo continues, 여기서 echo success성공하지만, 오류를 범할 명령을 나타내고, 출력 할 것이다 success, piping그리고 continues, 그러나 false | cat - <(echo piping); echo continues함께, false여전히 인쇄됩니다, 이제 명령을 나타내는 자동으로 erroring piping종료하기 전에.
bb010g

54

bash에 따라 -Set Builtin manual, -e/ errexit가 설정되면 단일 간단한 명령 , 목록 또는 복합 명령 으로 구성된 파이프 라인이 있으면 쉘이 즉시 종료됩니다. 반환이 아닌 제로 상태입니다.

기본적으로 파이프 라인의 종료 상태는 파이프 라인에서 마지막 명령의 종료 상태입니다. pipefail옵션이 활성화되어 (기본적으로 비활성화되어 있음) .

그렇다면 파이프 라인의 마지막 (가장 오른쪽) 명령의 리턴 상태가 0이 아닌 상태로 종료되거나 모든 명령이 성공적으로 종료되면 0입니다.

종료시 무언가를 실행하려면 다음과 같이 정의하십시오 trap.

trap onexit EXIT

여기서 onexit같은 그 이하 간단한 인쇄되고, 출구에 뭔가를 할 수있는 함수입니다 스택 추적 :

onexit(){ while caller $((n++)); do :; done; }

비슷한 옵션 -E/가errtrace 있으며 대신 ERR을 잡을 수 있습니다.

trap onerr ERR

제로 상태 예 :

$ true; echo $?
0

0이 아닌 상태 예 :

$ false; echo $?
1

부정 상태 예 :

$ ! false; echo $?
0
$ false || true; echo $?
0

pipefail비활성화 된 상태에서 테스트 :

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

pipefail활성화 된 상태에서 테스트 :

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

54

로 인해 중단 된 스크립트의 종료 상태가 무엇인지 파악하려고 시도하는 동안이 게시물을 찾았습니다 set -e. 대답은 나에게 명백하지 않았다. 따라서이 답변. 기본적으로 set -e명령 실행 (예 : 셸 스크립트)을 중단하고 실패한 명령의 종료 상태 코드 (예 : 외부 스크립트가 아닌 내부 스크립트)를 반환합니다. .

예를 들어, 쉘 스크립트가 있다고 가정하십시오 outer-test.sh.

#!/bin/sh
set -e
./inner-test.sh
exit 62;

코드 inner-test.sh는 다음과 같습니다.

#!/bin/sh
exit 26;

outer-script.sh명령 줄에서 실행하면 외부 스크립트가 내부 스크립트의 종료 코드로 종료됩니다.

$ ./outer-test.sh
$ echo $?
26

10

그 의도는 문제의 스크립트가 빠르게 실패하는 것이라고 생각합니다.

직접 테스트하려면 set -ebash 프롬프트에서 입력하십시오 . 이제 실행 해보십시오 ls. 디렉토리 목록이 나타납니다. 이제을 입력하십시오 lsd. 이 명령은 인식되지 않고 오류 코드를 반환하므로 bash 프롬프트가 닫힙니다 (로 인해 set -e).

이제 '스크립트'의 맥락에서 이것을 이해하려면 다음 간단한 스크립트를 사용하십시오.

#!/bin/bash 
# set -e

lsd 

ls

그대로 실행 ls하면 마지막 줄 에서 디렉토리 목록을 얻을 수 있습니다. 의 주석을 해제하고 set -e다시 실행하면 bash에서 오류가 발생하면 bash가 처리를 중지하므로 디렉토리 목록이 표시되지 않습니다 lsd.


이 답변은 질문에 대해 다른 사람에게 제공되지 않은 통찰력이나 정보를 추가합니까?
Charles Duffy

6
다른 답변에는없는 기능에 대한 간결하고 간결한 설명을 제공한다고 생각합니다. 다른 답변보다 더 집중된 것은 없습니다.
Kallin Nagelberg

9

이것은 오래된 질문이지만 데비안 패키지 처리 스크립트에서 set -e일명 사용에 대해서는 여기에 답변이 없습니다 set -o errexit. 이 스크립트에서는 데비안 정책에 따라이 옵션을 반드시 사용해야합니다 . 의도는 처리되지 않은 오류 조건의 가능성을 피하기위한 것입니다.

이것이 실제로 의미하는 것은 실행하는 명령이 오류를 반환 할 수있는 조건을 이해하고 각 오류를 명시 적으로 처리해야한다는 것입니다.

일반적인 문제는 (예 : diff차이 grep가있을 때 오류를 반환 ) 및 (일치가 없으면 오류를 반환)입니다. 명시 적 처리로 오류를 피할 수 있습니다.

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(메시지에 현재 스크립트 이름을 포함시키고 표준 출력 대신 표준 오류에 진단 메시지를 쓰는 방법에주의하십시오.)

명시 적 처리가 실제로 필요하거나 유용하지 않으면 명시 적으로 아무 것도 수행하지 마십시오.

diff this that || true
grep cat food || :

(셸의 :no-op 명령 사용은 약간 애매하지만 상당히 일반적입니다.)

반복해서 말하면

something || other

속기

if something; then
    : nothing
else
    other
fi

other, something실패 할 경우에만 실행해야한다고 명시 적으로 말하고 있습니다 . 긴 형식 if(와 같은 다른 쉘 흐름 제어 문은 while, until그것이 아니었다면),도와 쉘 스크립트 참으로 (오류를 처리 할 수있는 유효한 방법이다set -e 흐름 제어 문을 포함하지 않을 수 있습니다!)

또한 명시 적으로, 이와 같은 핸들러 set -e가 없으면 diff차이점을 grep찾거나 일치를 찾지 못하면 전체 스크립트가 즉시 오류와 함께 실패 합니다.

반면에 일부 명령은 원하는 경우 오류 종료 상태를 생성하지 않습니다. 일반적으로 문제가되는 명령은 find(종료 상태는 파일이 실제로 발견되었는지 여부를 반영하지 않음) 및 sed(종료 상태는 스크립트가 입력을 받았는지 또는 실제로 명령을 성공적으로 수행했는지 여부를 나타내지 않음)입니다. 일부 시나리오에서 간단한 가드는 출력이 없으면 소리를 지르는 명령으로 파이프하는 것입니다.

find things | grep .
sed -e 's/o/me/' stuff | grep ^

파이프 라인의 종료 상태는 해당 파이프 라인에서 마지막 명령의 종료 상태입니다. 따라서 위의 명령은 실제로 find및 의 상태를 완전히 숨기고 최종적으로 성공 sed했는지 여부 만 알려줍니다 grep.

(물론 Bash는 set -o pipefail;하지만 데비안 패키지 스크립트는 Bash 기능을 사용할 수 없습니다. 정책 sh은 이러한 스크립트 에 POSIX 를 사용하도록 강력하게 규정하고 있지만 항상 그런 것은 아닙니다.)

많은 상황에서 이것은 방어 적으로 코딩 할 때 별도로주의해야 할 사항입니다. 때로는 예를 들어 임시 파일을 통해 가야하므로 관용구와 편의가 쉘 파이프 라인을 사용하도록 지시하더라도 출력을 생성 한 명령이 성공적으로 완료되었는지 확인할 수 있습니다.


이것은 훌륭한 답변입니다. 모범 사례를 장려합니다. 나는 grep 명령에서 exactally 같은 문제를했고 내가 정말 '세트 -e'제거하지 않았다
미니

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.