서브 쉘에서 쉘 스크립트 종료


30

이 스 니펫을 고려하십시오.

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

일반적으로 func호출되면 스크립트가 종료되며 이는 의도 된 동작입니다. 그러나 다음과 같은 하위 셸에서 실행되는 경우

result=`func`

스크립트를 종료하지 않습니다. 이것은 호출 코드가 매번 함수의 종료 상태를 확인해야 함을 의미합니다. 이것을 피할 수있는 방법이 있습니까? 이것이 무엇 set -e입니까?


1
메시지를 stderr에 출력하고 스크립트를 중지시키는 "stop"함수를 원하지만, stop과 같은 함수가 예제와 같이 서브 쉘에서 실행될 때 멈추지 않습니다.
어니스트 AC

2
물론 현재 쉘이 아닌 서브 쉘에서 나옵니다. 함수를 직접 호출하면됩니다 : func.

1
변수에 저장
어니스트 AC

1
@ErnestAC 원래 질문에있는 모든 세부 사항을 제공하십시오. 위 함수는 문자열을 반환하지 않습니다.

1
@htor 나는 예를 바꿨다
어니스트 AC

답변:


10

당신은 수있는 원래의 쉘 (죽일 kill $$호출하기 전에) exit, 그리고 일 아마 것입니다. 그러나:

  • 오히려 나에게 추한 것 같습니다
  • 거기에 두 번째 서브 쉘이 있으면 깨집니다. 즉, 서브 쉘 내부에 서브 쉘을 사용하십시오.

대신 몇 가지 방법 중 하나를 사용 하여 Bash FAQ의 값을 다시 전달할 수 있습니다 . 불행히도 그들 중 대부분은 그렇게 크지 않습니다. 각 함수 호출 후 -e많은 문제가있는 경우 오류 검사가 중단 될 수 있습니다 . 또는 Perl로 전환하십시오.


5
감사. 그래도 오히려 파이썬으로 바꾸고 싶습니다.
어니스트 AC

2
내가 쓰는 것처럼, 그것은 2019 년입니다. 누군가에게 "펄로 전환"한다고 말하는 것은 말도 안됩니다. 논쟁의 여지가 있지만 죄송하지만 'C'에 대해 좌절감을 느낀 사람에게 IMO와 동등한 Cobol로 전환 하시겠습니까? 어니스트가 지적했듯이 파이썬이 훨씬 더 나은 선택입니다. 내가 선호하는 것은 Ruby입니다. 어느 쪽이든, 펄 이외의 것.
Graham Nicholls

38

예를 들어 종료 상태 77은 하위 수준의 하위 쉘을 종료한다는 의미이며

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -EERR트랩 과 함께 set -e사용하면 자체 오류 처리를 정의 할 수 있다는 점에서 개선 된 버전과 약간 비슷합니다 .

zsh을에서, ERR 트랩은 당신이 필요가 없습니다, 자동으로 상속됩니다 set -E, 당신은 또한 트랩 정의 할 수 있습니다 TRAPERR()기능을하고이를 통해 수정 $functions[TRAPERR]같이,functions[TRAPERR]="echo was here; $functions[TRAPERR]"


1
재미있는 해결책! 보다 더 우아합니다 kill $$.

3
주의해야 할 사항은이 트랩은 보간 된 명령을 처리하지 않는 것입니다 echo "$(exit 77)". 대본은 우리가 쓴 것처럼 계속 될 것입니다echo ""
Warbo

방해! -E가없는 (아주 오래된) bash에 행운이 있습니까? USER 신호에 트랩을 정의하고 해당 신호에 킬 (kill)을 사용해야 하는가? 나도 몇 가지 연구를 할 것입니다 ...
Olivier Dulac

서브 쉘 트랩에 있지 않을 때 77 대신 1을 반환하는 방법을 아는 방법은 무엇입니까?

7

에 대한 대안으로 kill $$시도 할 수도 있습니다. kill 0중첩 된 서브 쉘의 경우 작동합니다 (모든 발신자와 사이드 프로세스가 신호를 수신합니다) ...하지만 여전히 잔인하고 추악합니다.


2
이 프로세스는 프로세스 0을 종료하지 않습니까?
어니스트 AC

5
그것은 전체 프로세스 그룹을 죽일 것입니다. 원하지 않는 것을 칠 수 있습니다 (예를 들어 백그라운드에서 몇 가지를 시작한 경우).
derobert

2
@ErnestAC는 kill (2) 맨 페이지를 참조하십시오. pids ≤0은 특별한 의미가 있습니다.
derobert

0

이 시도 ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "shell..."
func $1

echo "subshell..."
result=`func $1`

echo "shell..."
echo "result=$result"

내가 얻는 결과는 ...

# test_exitsubshell true
shell...
foo
subshell...
shell...
result=foo
# test_exitsubshell false
shell...
something went wrong

노트

  • 허용하는 파라미터 if테스트로 true또는 false(은 2 개 실행 참조)
  • if테스트는 false, 우리는 서브 쉘에 도달하지 않았다.

이것은 사용자가 게시 한 원래 아이디어와 매우 유사하며 작동하지 않는다고 말했습니다. 나는 이것이 서브 쉘 경우에 효과가 없다고 생각합니다. "shell"사례 다음에 false를 사용한 테스트는 "subshell"테스트 사례에 도달하지 않습니다. 서브 쉘이 "exit 1"호출을 종료하지만 오류를 외부 쉘로 전파하지 않기 때문에이 경우 실패 할 것이라고 생각합니다.
stuckj

0

(Bash specific answer) Bash에는 예외 개념이 없습니다. 그러나 set -o errexit (또는 동등한 항목 : set -e)를 최 외곽 레벨로 설정하면 실패한 명령으로 인해 서브 쉘이 0이 아닌 종료 상태로 종료됩니다. 이것이 서브 쉘 실행 주위에 조건이없는 중첩 된 서브 쉘 세트 인 경우, 전체 스크립트를 효과적으로 '롤업'하고 종료합니다.

다양한 bash 코드 비트를 더 큰 스크립트에 포함하려고 할 때 까다로울 수 있습니다. 하나의 bash 덩어리는 저절로 잘 작동하지만 errexit (또는 errexit없이)에서 실행될 때 예기치 않은 방식으로 동작합니다.

[192.168.13.16 (f0f5e19e) ~ 22:58:22] # bash -o errexit / tmp / foo
무언가 잘못되었다
[192.168.13.16 (f0f5e19e) ~ 22:58:31] # bash / tmp / foo
무언가 잘못되었다
그러나 우리는 어쨌든 여기에 도착했다
[192.168.13.16 (f0f5e19e) ~ 22:58:37] # cat / tmp / foo
#! / bin / bash
중지 () {
    에코 "$ {1}"
    1 번 출구
}

거짓이면; 그때
    에코 "foo"
그밖에
    (
        "뭔가 잘못되었다"그만해
    )
    echo "하지만 어쨌든 여기 도착했습니다"
fi
[192.168.13.16 (f0f5e19e) ~ 22:58:40] #

-2

하나의 라이너로 나가는 예제 :

COMAND || ( echo "ERROR – executing COMAND, exiting..." ; exit 77 );[ "$?" -eq 77 ] && exit

1
이것은 실제로 OP의 요청에 따라 하위 셸에서 실행되는 명령과 작동하는 답변이 아닌 것 같습니다 ... 의견이나 이유가없는 다운 투표는 나쁜 답변만큼 도움이되지 않습니다.
DVS
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.