예

일부 데이터가 파일에 포함되어 있는지 확인하고, 실패하지 않으면 다음과 같은 기본 스크립트를 유지하기 위해 단순화된 함수를 호출할 수 있는 bash 스크립트를 갖고 싶습니다. 이게 딱 맞아요.

이것은 작동하지 않습니다(하위 쉘이 실패하면 기본 스크립트를 종료하지 않습니다).

require_line다음과 같은 파일에 20개가 넘는 함수를 말할 수 있도록 함수를 어떻게 작성해야 합니까?

VALUE1=$(require_line "myKey1")
VALUE2=$(require_line "myKey2")
...

각 주변에 if가 필요하지 않습니까?

#!/bin/bash
set -eo pipefail

VALUE=$(require_line "myKey")

require_line(){
  local KEY=$1
  local DATA=$(cat /tmp/myfile)
  local INFO=$(echo "$DATA" | grep "$KEY")

  if [ ! -z "$INFO" ]
  then
    echo "Key not found in $DATA, key: $KEY"
    exit 1;
  fi
  echo "$INFO"
}

답변1

하위 쉘을 종료해도 경험했던 것처럼 기본 스크립트가 종료되지 않습니다.

나는 3가지(반)의 해결책을 생각합니다.

  1. set -e("테스트되지 않은") 실패한 명령(또는 하위 쉘)이 즉시 기본 스크립트를 종료하도록 사용하십시오 (이는 과잉이거나 다른 문제를 일으킬 수 있음).
  2. 함수에서 신호를 보내고 이를 포착합니다.trap
  3. 다음과 같이 || exit $?각각 후에 사용하십시오 .VALUEn=$(...)VALUE=$(require_line "myKey") || exit $?
  4. 를 사용하여 (3.)을 (그다지 우아하지는 않은) 루프와 결합합니다 eval.

세 번째 항목은 정확히 "각 항목 주위에 if가 필요"하지 않으며 여전히 상당히 간결한 구문 IMHO입니다.


그런데, 이 줄

echo "Key not found in $DATA, key: $KEY"

$VALUEn... 문장이 표시되지 않는 변수 에 저장되기 때문에 전체 스크립트를 바로 종료하면 실제로 쓸모가 없습니다.

다음과 같이 인쇄하는 것이 좋습니다 stderr.

echo "my error" 1>&2

솔루션 1의 예

#!/bin/sh
set -e

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

VALUE1=$(myfunc "OK") 
echo $VALUE1
VALUE2=$(myfunc "NO WAY") 
echo $VALUE2

echo "main script did not exit"
$ ./test.sh
OK
zsh: exit 1     ./test.sh

set -e그러나 처음부터 제거하면 다음과 같은 결과가 나타납니다.

$ ./test.sh
OK
NO WAY
main script did not exit

솔루션 2의 예

#!/bin/sh

trap "exit $?" USR1

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then kill -USR1 $$ ; fi 
}

VALUE1=$(myfunc "OK") 
echo $VALUE1
VALUE2=$(myfunc "NO WAY")
echo $VALUE2

echo "main script did not exit"
$ ./test.sh
OK

솔루션 3의 예

#!/bin/sh

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

VALUE1=$(myfunc "OK") || exit $?
echo $VALUE1
VALUE2=$(myfunc "NO WAY") || exit $?
echo $VALUE2

echo "main script did not exit"
$ ./test.sh
OK
zsh: exit 1     ./test.sh

솔루션 4의 예

#!/bin/sh

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

I=1
for key in "OK" "OK" "NO WAY": ; do
        eval "VALUE$I=\$(myfunc \"$key\")" || exit $?
        eval "echo \$VALUE$I"
        I=$(($I+1))
done
echo "main script did not exit"
$ ./test.sh
OK
OK
zsh: exit 1     ./test.sh

답변2

require_line도움이 되도록 스크립트를 약간 재구성해 보겠습니다 .

첫째, 쓸모없는 cat | grep. 둘째, grep의 고유 동작을 사용하여 에 대한 검색의 성공 또는 실패를 나타낼 수 KEY있을 뿐만 아니라 키가 발견되면 에 인쇄할 수 있습니다 stdout.

require_line(){
  local KEY="$1"
  local FILE="/tmp/myfile"

  if grep "$KEY" "$FILE"
  then
    return 0
  else
    printf 'Key not found in:\n\n"%s"\n\nKey: "%s"\n' "$(cat "$FILE")" "$KEY" >&2
    return 1
  fi
}

이는 의 내장 동작을 활용합니다 grep. 키가 발견되면 grep일치하는 줄을 인쇄하고 성공을 반환합니다. 그렇지 않으면 else분기가 수행되고 키를 찾을 수 없다는 메시지가 인쇄됩니다. 또한 실패할 경우 grep오류 메시지가 에 인쇄되어 stderr오류 메시지가 에서 발견된 유효한 일치 항목으로 오인되지 않도록 합니다 $FILE.

읽을 행을 변경한 다음 호출할 때마다 원하는 파일 이름을 전달하여 require_line파일 이름을 매개변수로 허용하도록 추가로 수정할 수 있습니다 .$2local FILE="$2"require_line

이제 그 자리에....

KEYn에 대해 각 VALUEn을 실제로 저장해야 합니까, 아니면 VALUEn이 모두 존재하는지 확인해야 합니까?

require_line이제 성공 또는 실패 값을 명확하게 반환하는 함수가 있으므로 간단히 AND모든 테스트를 함께 수행할 수 있습니다. 그 중 하나라도 실패하면 전체 테스트가 실패하게 됩니다.

실제 일치 값이 필요하다고 가정하면 이를 수행하는 장황한 방법은 다음과 같습니다.

if value1=$(require_line "key1") &&
   value2=$(require_line "key2") &&
   value3=$(require_line "key3")
then
   printf "%s\n" "$value1" "$value2" "$value3"
else
   printf "One or more keys failed.\n" >&2
fi

확인할 키가 많으면 지루할 것입니다. 배열을 사용하는 것이 더 나을 수도 있습니다.

#!/usr/bin/env bash

require_line(){
  local KEY="$1"
  local FILE="/tmp/myfile" # or perhaps "$2"

  if grep "$KEY" "$FILE"
  then
    return 0
  else
    printf 'Key not found in:\n\n"%s"\n\nKey: "%s"\n' "$(cat "$FILE")" "$KEY" >&2
    return 1
  fi
}

declare keys=("this" "is" "a" "test" "\." "keyN")
N=${#keys[@]}

declare values=()

j=0
while [ $j -lt $N ] && values[$j]="$(require_line "${keys[j]}")"
do
  j=$(($j+1))
done

if [ $j -lt $N ]
then
  printf 'error: found only %d keys out of %d:\n' $j $N
  printf '  "%s"\n' "${values[@]}"
fi

일부 샘플 데이터를 사용하여 해당 코드를 실행합니다.

$ cat /tmp/myfile
this is a test.
$ ./test.sh 
Key not found in:

"this is a test."

Key: "keyN"
error: found only 5 keys out of 6:
  "this is a test."
  "this is a test."
  "this is a test."
  "this is a test."
  "this is a test."
  ""

마지막으로 일치 값이 무엇인지 알 필요 없이 모든 키가 존재하는지 확인하기만 하면 되는 경우 위의 배열 지향 코드를 단순화하여 모든 키를 찾을 때까지 단순히 반복하거나 첫 번째 키에서 중단할 수 있습니다. 누락된 것으로 확인됩니다.

관련 정보