Bash 스크립트에서 코드 블록을 조기에 종료하는 방법(Perl의 "마지막" 지시문과 유사)

Bash 스크립트에서 코드 블록을 조기에 종료하는 방법(Perl의 "마지막" 지시문과 유사)

내가 정말 좋아하는 Perl의 기능 중 하나는 루프 제어 키워드의 일반화입니다.어느중괄호로 구분된 어휘 덩어리 1 .

예를 들어, last다음 장난감 스크립트에 표시된 것처럼 Perl의 명령을 사용하여 해당 블록을 종료할 수 있습니다.

#!/usr/bin/env perl

use strict;

my $value = $ARGV[0] || 0;
my $overall_status = 'failed';
{
  $value > 0 and print "$value > 0\n" or last;
  $value > 1 and print "$value > 1\n" or last;
  $value > 2 and print "$value > 2\n" or last;
  $value > 3 and print "$value > 3\n" or last;
  $overall_status = 'ok';
}

die 'EARLY EXIT' unless $overall_status eq 'ok';

print "ok\n";

이 예에서 중괄호로 구분된 블록의 처음 4개 문은 다음을 나타냅니다.일련의 필수 조건: 실패어느둘 중 하나는 오류 메시지를 인쇄하고(내용은 실패 조건과 무관함) 스크립트 실행을 종료해야 합니다.

제가 강조하고 싶은 것은블록 내에서 발생할 수 있는 모든 오류에 대한 공통 응답(이 경우 이 응답은 종료 후 발생할 수 있는 모든 실패 지점을 구분하지 않는 일반적인 오류 메시지입니다.)문제의 중요하고 협상할 수 없는 측면입니다.. 사실, 코드 블록의 어느 곳에서든 종료할 수 있도록 하는 것이 문제의 한 측면이라고 생각합니다.

중요한:위의 예를 이해하기 쉽게 만들기 위해 조건을 인위적으로 단순하게 만들었습니다. 그러나 실제로는각 조건을 평가하는 데는 여러 줄의 코드가 필요할 수 있습니다! 답을 공식화할 때 이 점을 명심하십시오. 이 예제의 요점은 코드 블록이 블록 내의 어느 곳에서나 종료될 수 있다는 것입니다.


Bash 스크립트에서 비슷한 효과를 얻을 수 있는 좋은 방법을 찾고 있습니다.

나는 이것보다 더 좋은 것을 생각할 수 없습니다:

#!/bin/bash

overall_status=failed
value=$1
while true; do
    (( value > 0 )) && echo "$value > 0" || break;
    (( value > 1 )) && echo "$value > 1" || break;
    (( value > 2 )) && echo "$value > 2" || break;
    (( value > 3 )) && echo "$value > 3" || break;
    overall_status=ok
    break
done

if [[ "$overall_status" != ok ]]; then
    echo 'EARLY EXIT' >&2
    exit 1
fi

while... 루프를 의도하지 않은 루프 구문()을 사용하기 때문에 혼란스럽습니다 .

zsh 스크립트에서 적어도 이상하게 보이는 루프를 while일회성 익명 함수 2 로 바꿀 수 있습니다.

() {
    (( value > 0 )) && echo "$value > 0" || return 1;
    (( value > 1 )) && echo "$value > 1" || return 1;
    (( value > 2 )) && echo "$value > 2" || return 1;
    (( value > 3 )) && echo "$value > 3" || return 1;
    overall_status=ok
}

나는 zsh 스크립트에서 코드 블록의 조기 종료를 구현하는 다른 방법을 배우는 데 여전히 관심이 있습니다.


1 이 빠른 메모리 기반 설명은 약간 지나치게 단순화될 수 있습니다...
2 실제로 이 접근 방식을 사용하면 overall_status변수를 완전히 생략하고 테스트 할 수도 있습니다 $?.

편집하다:Perl 예제 뒤에 "중요:..." 메모를 추가했습니다.

편집 2:블록 내의 모든 결함에 대한 공통 대응을 강조하는 것은 문제의 중요한 측면입니다.

답변1

제목의 질문을 피하고 특정 오류 종료 사례에 초점을 맞추기 위해 전체 오류 메시지와 종료를 함수에 넣고 breaking 대신 직접 호출합니다. 이는 또한 기본 코드 경로에서 추가 블록과 들여쓰기 수준을 방지합니다.

#!/bin/bash
die() {
    echo 'error: mandatory conditions not met!' >&2
    exit 1
}
value=$1
(( value > 0 )) && echo "$value > 0" || die;
(( value > 1 )) && echo "$value > 1" || die;
(( value > 2 )) && echo "$value > 2" || die;
(( value > 3 )) && echo "$value > 3" || die;

분명히 조건이 있는 블록 이후에 실행을 계속하려는 경우에는 작동하지 않습니다. 이 경우 빈 루프가 필요합니다. 단 하나의 항목만 포함하는 루프는 for한 번의 반복 후에 자연스럽게 종료되므로 여기서는 논리적일 수 있습니다(그러나 루프 변수의 이름을 지정해야 함).

for __ in once; do
    echo this prints only once
    (( value > 0 )) && echo "$value > 0" || break;
    ...
done
echo continuing from here

일반적으로 사용자가 알 수 있도록 각 오류 조건에 대해 다른 오류 메시지를 제공하는 것이 좋습니다.어느요청이 실패했습니다. 이는 die메시지를 매개변수로 전달하여 수행할 수 있습니다. 어떤 경우에는 종료하기 전에 검사 중 하나가 실패했는지 여부에 관계없이 모든 검사를 수행하는 것도 가능합니다. 이렇게 하면 사용자가 모든 문제를 동시에 인식할 수 있습니다.

#!/bin/bash
errexit=
error() {
    echo "error: $1" >&2
    errexit=1   # global
}
value=$1
(( value > 0 )) && echo "$value > 0" || error 'value <= 0';
(( value > 1 )) && echo "$value > 1" || error 'value <= 1';
(( value > 2 )) && echo "$value > 2" || error 'value <= 2';
(( value > 3 )) && echo "$value > 3" || error 'value <= 3';
[ "$errexit" ] && exit 1

답변2

나는 Bash에서 당신이 원하는 것을 성취할 수 있는 어떤 방법도 모릅니다(당신이 식별한 옵션 외에).

당신이 원하는 것을 내가 하려고 한다면 아마도 다음과 같이 할 것입니다.

#!/bin/bash

overall_status=failed
value=$1

if   (( value > 0 )); then echo "value > 0"
elif (( value > 1 )); then echo "value > 1"
elif (( value > 2 )); then echo "value > 2"
elif (( value > 3 )); then echo "value > 3"
else overall_status=ok
fi

if [[ "$overall_status" != ok ]]; then
    echo 'EARLY EXIT' >&2
    exit 1
fi

답변3

명령 그룹을 일찍 종료하는 쉬운 방법은 다음과 같습니다.그리고 목록&&or 문에 대한 짧은 대안 인 첫 번째 실패 시 중단할 항목을 사용합니다 . 예제의 더미 while 루프를 다음으로 바꾸세요.ifcase

(( value > 0 )) && echo "$value > 0" &&
(( value > 1 )) && echo "$value > 1" &&
(( value > 2 )) && echo "$value > 2" &&
(( value > 3 )) && echo "$value > 3" &&
overall_status=ok

또는 zsh 익명 함수와 유사하게 명명된 bash 함수가 없는 이유는 무엇입니까?

#!/bin/bash

arg=$1

function f() {
    value=$1
    (( value > 0 )) && echo "$value > 0" || return 1
    (( value > 1 )) && echo "$value > 1" || return 1
    (( value > 2 )) && echo "$value > 2" || return 1
    (( value > 3 )) && echo "$value > 3" || return 1
    return 0
}

if f arg; then
    exit 0
else
    echo 'EARLY EXIT' >&2
    exit 1
fi

답변4

당신도 같은 방식으로 수행합니다.

Perl에서 {...}블록은 한 번만 실행되는 루프입니다. 따라서 쉘에서 동일한 작업, 즉 한 번만 실행되는 루프를 수행합니다. break대신 사용하십시오 last:

for _ in "$_"; do
    foo || break
    bar || break
    baz || break
    {
        quux
        moo
    } || break     
done

$value > 0 and print "$value > 0\n" or last;

foo && bar || quux이는 쉘에서와 같은 이유로 좋지 않습니다. 실패 하면 print어떻게 되나요 ? 쉘과 달리 Perl에는 삼항 연산자가 있습니다 ?:. 그걸 써:

$value > 0 ? print "$value > 0\n" : last;

if(...){...}else{...}또는 너무 간결하다고 생각되면 명시적 표현을 사용하세요.

더욱이 이것은 #!/usr/bin/env perl문제를 요구하고 있습니다. /usr/bin/perl배포판에서 확인할 수 있는 버전 뿐만 아니라 사용자 경로에 있을 수 있는 모든 가능한 Perl 버전에 대해 스크립트를 테스트해야 하는 이유는 무엇입니까 ?

관련 정보