중첩된 루프가 있는 쉘 스크립트가 있는데 "종료"가 실제로 스크립트를 종료하지 않고 현재 루프만 종료한다는 것을 발견했습니다. 일부 오류 상황에서 스크립트를 완전히 종료하는 다른 방법이 있습니까?
허용 가능한 오류가 있고 너무 많은 재작성을 필요로 하기 때문에 "set -e"를 사용하고 싶지 않습니다.
지금은 프로세스를 수동으로 종료하기 위해 kill을 사용하고 있지만 이를 수행하는 더 좋은 방법이 있어야 할 것 같습니다.
답변1
귀하의 문제는 중첩 루프 자체가 아닙니다. 이것은 내부 루프가 있는 곳입니다.서브쉘에서 실행.
이것은 작동합니다:
#!/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에서 제공됩니다. 좋은 하루 보내세요. :-)
답변2
이전 답변에서는 사용을 제안했지만 [[ $? != 0 ]] && exit $?
그렇지 않습니다.상당히[[ $? != 0 ]]
테스트가 0으로 재설정되므로 예상대로 작동합니다 $?
. 즉, 스크립트가 예상대로 일찍 종료되더라도 (예상과 달리) 항상 코드 0으로 종료됩니다. 또한 문자열 비교 테스트 -ne
보다는 숫자 비교 테스트를 사용하는 것이 좋습니다 !=
. 따라서 IMHO 더 나은 솔루션은 다음을 사용하는 것입니다.
err=$?; [[ $err -ne 0 ]] && exit $err
이것이 보장하기 때문에실제종료 코드가 올바르게 설정되었습니다.
답변3
당신은 그것을 사용할 수 있습니다 break
.
에서 help break
:
Exit a FOR, WHILE or UNTIL loop. If N is specified, break N enclosing loops.
따라서 세 개의 닫힌 루프를 종료하려면, 즉 기본 루프에 두 개의 중첩 루프가 있는 경우 다음을 사용하여 모든 루프를 종료하십시오.
break 3
답변4
파이프라인의 종료 상태 코드(허용된 답변에 표시됨)를 테스트하는 대신 프로세스 대체를 사용하는 것입니다.
그래서 이것 대신에:
#!/usr/bin/env bash
seq 3 | while read line_1; do
seq 3 | while read line_2; do
if [ "$line_2" -eq 3 ]; then
echo 'Stop!'
exit 1
fi
echo "$line_1 $line_2"
done
done
이 작업을 수행:
#!/usr/bin/env bash
while read line_1; do
while read line_2; do
if [ "$line_2" -eq 3 ]; then
echo 'Stop!'
exit 1
fi
echo "$line_1 $line_2"
done < <(seq 3)
done < <(seq 3)
이 해결책을 찾았습니다여기. 이 답변에는 몇 가지 중요한 경고 사항도 언급되어 있습니다.
이
<( )
구성("프로세스 대체")은 모든 쉘에서 사용할 수 없으며 sh 호환 모드의 bash에서도 사용할 수 없습니다(즉,sh
or 로 호출 되는 경우/bin/sh
). 따라서 스크립트에서 명시적인 bash shebang(예:#!/bin/bash
또는 ) 을 사용 하고 명령으로 스크립트를 실행하여 이를 재정의하지 마십시오 .#!/usr/bin/env bash
sh