중첩 루프가있는 쉘 스크립트 종료


11

중첩 루프가있는 쉘 스크립트가 있는데 "exit"가 실제로 스크립트를 종료하지 않고 현재 루프 만 종료한다는 것을 알았습니다. 특정 오류 조건에서 스크립트를 완전히 종료하는 다른 방법이 있습니까?

허용되는 오류가 있고 너무 많은 재 작성이 필요하기 때문에 "set -e"를 사용하고 싶지 않습니다.

지금은 kill을 사용하여 프로세스를 수동으로 종료하고 있지만 더 좋은 방법이 있어야합니다.


1
"종료"가 실제로 스크립트를 종료하지 않는다는 것은 무엇을 의미합니까? 그냥 시도하십시오 bash -c 'for x in y z; do exit; done; echo "This never gets printed"'.
Chris Down

맞습니다. 일반적으로 중첩 루프에서 종료해야하지만 exit를 사용하면 스크립트가 외부 루프로 계속 진행됩니다. 스크립트를 게시 할 수 없습니다.
user923487 2016 년

2
왜 문제를 보여주는 스크립트를 작성하여 여기에 게시 할 수 없습니까? 그럴 것 같지 않습니다.
Toby Speight

1
내부 루프가 코드의 하위 셸에서 발생하는 경우입니까?
Toby Speight

@Toby 대부분의 스크립트는 로깅 목적으로 하위 셸에 있지만 루프와 나머지 코드는 모두 동일한 하위 셸에 있습니다.
user923487 2016 년

답변:


19

문제는 중첩 루프 자체가 아닙니다. 하나 이상의 내부 루프가 하위 쉘에서 실행 중입니다 .

이것은 작동합니다 :

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        for j in $(seq 1 10) ; do
                echo j $j
                sleep 1
                [[ $j = 3 ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done
echo "After all the loops."

산출:

i 1
j 1
j 2
j 3
I've had enough!

이것은 당신이 설명한 문제를 나타냅니다 :

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done    
echo "After all the loops."

산출:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 2
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 3
LINE root:x:0:0:root:/root:/bin/bash
(...etc...)

해결책은 다음과 같습니다. 서브 쉘에서 실행되는 내부 루프의 리턴 값을 테스트해야합니다.

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        err=$?; [[ $err != 0 ]] && exit $err
        echo "After the j loop."
done
echo "After all the loops."

테스트에 유의하십시오. [[ $? != 0 ]] && exit $?

산출:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!

편집 : 현재 사용중인 서브 쉘을 확인하려면 "응답"스크립트를 수정하여 현재 쉘의 프로세스 ID를 알려주십시오. 참고 : 이것은 bash 4에서만 작동합니다.

#!/bin/bash

for i in $(seq 1 100); do
        echo pid $BASHPID i $i
        cat /etc/passwd | while read line; do
                echo pid $BASHPID LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        err=$?; [[ $err != 0 ]] && echo pid $BASHPID && exit $err
        echo "After the j loop."
done
echo "After all the loops."

산출:

pid 31793 i 1
pid 31796 LINE root:x:0:0:root:/root:/bin/bash
pid 31796 LINE bin:x:1:1:bin:/bin:/sbin/nologin
pid 31796 LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
pid 31793

변수 "i"와 "j"는 Fortran에 의해 제공되었습니다. 좋은 하루 보내세요 :-)


내부 및 외부 루프가 동일한 하위 쉘에서 실행 중이므로 내부 루프를 종료하면 둘 다 종료해야합니까? 그래도 문제를 스스로 재현하는 데 어려움이 있습니다. 프로그램 논리를 대부분 제거하자마자 문제는 사라졌습니다. 어쨌든, 나는 이것을 무언가로 할 필요가 있기 때문에 지금은 대답으로 표시 할 것입니다.
user923487

제공하신 링크를 읽은 후 문제가있는 것 같습니다. 외부 루프에서 "cat file | while read line"을 수행하고 있습니다. 파이핑은 하위 쉘을 만듭니다. 나는 몰랐다.
user923487

@ user923487-업데이트 된 답변보기 bash 4가있는 경우 서브 쉘 pid를 에코 (또는 현대의 경우 printf)하고 서브 쉘에 있는지 여부를 확인할 수 있습니다. bash 버전을 보려면 bash --version명령 행에 입력하십시오 .
Mike S

매우 도움이됩니다. 감사! "killall scriptname.sh"가이 문제를 해결하는 가장 간단한 방법 인 것 같습니다.
user923487

2

이전 대답은 사용 제안 [[ $? != 0 ]] && exit $?그러나이하지 않습니다 확실히 예상대로 때문에, 작업을 [[ $? != 0 ]]테스트 재설정 $?, 제로로하는 수단을 백업하는 것이 비록 스크립트 것입니다 예상대로 조기 종료, 그것을거야 항상 코드 0으로 종료 (예상하지) . 또한 문자열 비교 테스트 -ne보다는 숫자 비교 테스트 를 사용하는 것이 좋습니다 !=. 따라서 IMHO의 더 나은 솔루션은 다음을 사용하는 것입니다.

err=$?; [[ $err -ne 0 ]] && exit $err

실제 종료 코드가 올바르게 설정되어 있는지 확인하십시오 .


좋은 피드백. 코드를 수정했습니다.
마이크 S

1

사용할 수 있습니다 break.

보낸 사람 help break:

Exit a FOR, WHILE or UNTIL loop.  If N is specified, break N enclosing loops.

따라서 세 개의 엔 클로징 루프를 종료하려면 (예 : 메인 루프 안에 두 개의 중첩 루프가있는 경우)이를 사용하여 모든 루프를 종료하십시오.

break 3

루프 후에는 더 많은 코드가 있으므로 그 자체만으로는 충분하지 않습니다.
user923487 2016 년

1
멋진 thx! 예for((i=0;i<3;i++));do echo A;for((j=0;j<2;j++));do echo B;break 2;done;done
물병 자리 힘

0

exit 전체 쉘 또는 현재 하위 쉘을 종료합니다.

$ bash -c 'for i in 1 2 3; do for j in 4 5 6; do echo $i; exit 1; echo $j; done; done'
1
$ echo $?
1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.