예

파일에 특정 데이터가 포함되어 있는지 확인하기 위해 함수를 호출할 수 있는 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을 실제로 저장해야 합니까, 아니면 모두 존재하는지 확인해야 합니까?

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."
  ""

마지막으로, 실제로 모든 키가 존재하는지 확인해야 하고 일치하는 값이 무엇인지 알 필요가 없다면 위의 배열 지향 코드를 모든 키를 찾을 때까지 단순히 반복하거나 중지하는 것으로 줄일 수 있습니다. 첫 번째 키에서 누락된 것이 발견되었습니다.

관련 정보