Unix 쉘 스크립트에 환경 변수가 설정되어 있는지 확인하는 간결한 방법은 무엇입니까?


500

작업을 시작하기 전에 특정 환경 변수가 설정되어 있는지 확인 해야하는 유닉스 셸 스크립트가 몇 가지 있습니다.

if [ -z "$STATE" ]; then
    echo "Need to set STATE"
    exit 1
fi  

if [ -z "$DEST" ]; then
    echo "Need to set DEST"
    exit 1
fi

많은 타이핑입니다. 환경 변수 세트가 설정되어 있는지 확인하기위한보다 우아한 관용구가 있습니까?

편집 :이 변수에는 의미있는 기본값이 없다는 것을 언급해야합니다. 설정되어 있지 않으면 스크립트가 오류가 발생합니다.


2
이 질문에 대한 많은 답변은 Code Golf Stack Exchange 에서 볼 수있는 것 같습니다 .
Daniel Schilling

답변:


587

파라미터 확장

분명한 대답은 특수한 형태의 매개 변수 확장 중 하나를 사용하는 것입니다.

: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}

또는 더 좋습니다 (아래의 '큰 따옴표 위치'섹션 참조).

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

첫 번째 변형 ( ?) 만 사용 하려면 STATE를 설정해야하지만 STATE = ""(빈 문자열)은 정상입니다. 정확히 원하는 것이 아니라 대체 및 이전 표기법입니다.

두 번째 변형 (을 사용 :?)은 DEST가 설정되고 비어 있지 않아야합니다.

메시지를 제공하지 않으면 쉘은 기본 메시지를 제공합니다.

${var?}구조는 버전 7 UNIX 및 Bourne 쉘 (1978 또는 그 부근)에 이식 돌아왔다. 이 ${var:?}구성은 약간 더 최신입니다. 1981 년경 System III UNIX에 있었지만 그 전에는 PWB UNIX에 있었던 것 같습니다. 따라서 Korn 쉘 및 특히 Bash를 포함한 POSIX 쉘에 있습니다.

일반적으로 쉘의 매뉴얼 페이지에 Parameter Expansion 이라는 섹션에 설명되어 있습니다. 예를 들어, bash매뉴얼은 다음 과 같이 말합니다.

${parameter:?word}

널 또는 설정되지 않은 경우 오류 표시 매개 변수가 널 (NULL)이거나 설정되지 않은 경우, 단어 확장 (또는 단어가없는 경우 해당 효과에 대한 메시지)이 표준 오류에 기록되고 쉘이 대화식이 아닌 경우 종료됩니다. 그렇지 않으면 매개 변수 값이 대체됩니다.

콜론 사령부

콜론 명령은 단순히 인수를 평가 한 다음 성공한다고 덧붙여 야합니다. 원래 쉘 주석 표기법입니다 ( ' #' 이전 줄 끝). 오랫동안 Bourne 쉘 스크립트에는 첫 문자로 콜론이있었습니다. C 셸은 스크립트를 읽고 첫 번째 문자를 사용하여 C 셸 ( ' #'해시) 또는 Bourne 셸 ( ' :'콜론) 에 해당하는지 확인합니다 . 그런 다음 커널은 행동을 #!/path/to/program취하고 ' '에 대한 지원을 추가 했으며 Bourne 쉘은 ' #'의견을 얻었 으며 콜론 규칙은 길가에있었습니다. 그러나 콜론으로 시작하는 스크립트를 발견하면 이유를 알 수 있습니다.


큰 따옴표의 위치

blong의견을 물었다 :

이 토론에 대한 의견이 있으십니까? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

토론의 요지는 :

… 그러나 shellcheck(버전 0.4.1)이 메시지가 나타납니다.

In script.sh line 13:
: ${FOO:?"The environment variable 'FOO' must be set and non-empty"}
  ^-- SC2086: Double quote to prevent globbing and word splitting.

이 경우 내가해야 할 일에 대한 조언이 있습니까?

짧은 대답은 " shellcheck제안대로"입니다.

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

이유를 설명하기 위해 다음을 연구하십시오. 점을 유의 :명령 인수를 (하지만 쉘이 인수를 평가 않음) 에코하지 않습니다. 우리는 인수를보고 싶기 때문에 아래 코드 printf "%s\n"는 대신에 사용 됩니다 :.

$ mkdir junk
$ cd junk
$ > abc
$ > def
$ > ghi
$ 
$ x="*"
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
abc
def
ghi
$ unset x
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
bash: x: You must set x
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
bash: x: You must set x
$ x="*"
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
*
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
abc
def
ghi
$ x=
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ unset x
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ 

전체 표현식이 큰 따옴표로 묶이지 않은 경우 값 $x이 먼저 확장 된 *다음 파일 이름 목록으로 확장되는 방법에 유의하십시오 . 이것이 shellcheck권장되는 사항입니다. 표현식이 큰 따옴표로 묶인 양식에 반대하지 않는지 확인하지는 않았지만 괜찮을 것이라는 합리적인 가정입니다.


2
그것이 내가 필요한 것입니다. ... 그냥 보여 간다 - 나는 1987 년부터 유닉스의 다양한 버전을 사용하고 난 다음 구문을 본 적이
AndrewR

이 작업은 어떻게 이루어지며 어디에 기록되어 있습니까? 변수가 존재하고 특정 값으로 설정되어 있는지 확인하도록 수정할 수 있는지 궁금합니다.
jhabbott

6
쉘 매뉴얼 페이지 또는 Bash 매뉴얼에 일반적으로 'Parameter Expansion'헤더 아래에 설명되어 있습니다. 변수가 존재하고 특정 값 ( 'abc')으로 설정되어 있는지 확인하는 표준 방법은 다음과 같습니다 if [ "$variable" = "abc" ]; then : OK; else : variable is not set correctly; fi.
Jonathan Leffler

공백이있는 문자열 변수와 함께이 구문을 어떻게 사용합니까? 확인하려는 변수를 "This is a test"로 설정하면 "This : command not found"라는 오류가 발생합니다. 어떤 아이디어?
user2294382

1
이 토론에 대한 의견이 있으십니까? github.com/koalaman/shellcheck/issues/…
blong

138

이 시도:

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;

8
오히려 장황하다. 세미콜론은 중복됩니다.
Jonathan Leffler

3
또는 더 짧은 이 블로그 게시물을[ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; } 참조하십시오
zipizap

36
즉, 읽을 수 있습니다. 나는이 모든 것입니다. 배쉬 스크립트는 해당 부서에서 얻을 수있는 모든 도움이 필요합니다!
Iain 던컨

원래의 대답은 구문에서보다 일관성이 있습니다.
빠른 이빨

4
그러나 이것은 설정되지 않은 변수와 빈 문자열로 설정된 변수를 구별하지는 않습니다.
chepner

57

귀하의 질문은 사용중인 쉘에 따라 다릅니다.

Bourne shell은 당신이 쫓는 방식에 거의 영향을 미치지 않습니다.

그러나...

거의 모든 곳에서 작동합니다.

그냥 csh에서 멀리 떨어지십시오. 그것은 Bourne 껍질과 비교하여 종과 휘파람에 좋았지 만 지금은 삐걱 거리고 있습니다. 당신이 나를 믿지 않는다면, 그냥 csh에서 STDERR을 분리하십시오! (-:

여기에는 두 가지 가능성이 있습니다. 위의 예, 즉 다음을 사용합니다.

${MyVariable:=SomeDefault}

처음으로 $ MyVariable을 참조해야합니다. 이것은 env를 취합니다. var MyVariable이며 현재 설정되어 있지 않은 경우 나중에 사용할 수 있도록 SomeDefault 값을 변수에 할당합니다.

또한 다음과 같은 가능성이 있습니다.

${MyVariable:-SomeDefault}

이 구문을 사용하는 변수 대신 SomeDefault를 대체합니다. 변수에 SomeDefault 값을 지정하지 않으며이 명령문이 발생한 후에도 MyVariable의 값은 여전히 ​​널입니다.


3
CSH : (foo> foo.out)> & foo.err
Mr.Ree

Bourne 쉘은 필요한 작업을 수행합니다.
Jonathan Leffler

51

확실히 가장 간단한 방법은 -u스위치를 shebang (스크립트 상단의 줄)에 추가하는 것입니다 bash.

#!/bin/sh -u

바인딩되지 않은 변수가 숨어 있으면 스크립트가 종료됩니다.


40
${MyVariable:=SomeDefault}

MyVariable설정되고 null이 아닌 경우 변수 값이 재설정됩니다 (= 아무 일도 일어나지 않음).
그렇지 않으면 MyVariable로 설정되어 SomeDefault있습니다.

위의 방법은을 실행하려고 시도 ${MyVariable}하므로 변수를 설정하려는 경우 :

MyVariable=${MyVariable:=SomeDefault}

이것은 메시지를 생성하지 않으며 Q는 기본값이 없다고 말합니다 (답을 입력했을 때 그렇게 말하지는 않았습니다).
Jonathan Leffler

10
올바른 버전 : (1) : $ {MyVariable : = SomeDefault} 또는 (2) : MyVariable = $ {MyVariable : -SomeDefault}
Jonathan Leffler

23

제 생각에는 #! / bin / sh에 대한 가장 간단하고 호환 가능한 검사 는 다음과 같습니다.

if [ "$MYVAR" = "" ]
then
   echo "Does not exist"
else
   echo "Exists"
fi

이 역시 / bin / sh 용이며 이전 Solaris 시스템에서도 호환됩니다.


이 '작품'하지만, 심지어 오래된 Solaris 시스템은이 /bin/sh지원이 ${var:?}표기 등
조나단 레플러

지금까지 가장 좋은 답변입니다.
Ole

4
빈 문자열로 설정되는 것은 전혀 설정되지 않은 것과 다릅니다. 이 테스트는 모두 후 "합니까 존재하지"인쇄 것이라고 unset MYVAR하고 MYVAR=.
chepner

@ chepner, 아마도 귀중한 헤드 업에 대한 귀하의 의견을 옹호했지만 bash를 사용하여 테스트를 계속했으며 var를 빈 문자열로 설정할 수 없었습니다. 어떻게 하시겠습니까?
Sz.

@Sz 무슨 말인지 잘 모르겠습니다. "설정되지 않음"은 이름에 값이 할당되지 않았 음을 의미합니다. foo설정하지 않으면 "$foo"여전히 빈 문자열로 확장됩니다.
chepner

17

bash4.2 -v는 이름이 빈 문자열을 포함 하여 어떤 값으로 설정되어 있는지 테스트 하는 연산자를 도입했습니다 .

$ unset a
$ b=
$ c=
$ [[ -v a ]] && echo "a is set"
$ [[ -v b ]] && echo "b is set"
b is set
$ [[ -v c ]] && echo "c is set"
c is set

16

나는 항상 사용했다 :

if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi

훨씬 간결하지는 않습니다.

CSH에는 $? STATE가 있습니다.


2
이것은 STATE설정되지 않은 것이 아니라 비어 있거나 설정되어 있지 않은지 확인합니다 .
chepner

7

나 같은 미래의 사람들을 위해 한발 더 나아가 var 이름을 매개 변수화하고 싶기 때문에 변수 크기의 변수 이름 목록을 반복 할 수 있습니다.

#!/bin/bash
declare -a vars=(NAME GITLAB_URL GITLAB_TOKEN)

for var_name in "${vars[@]}"
do
  if [ -z "$(eval "echo \$$var_name")" ]; then
    echo "Missing environment variable $var_name"
    exit 1
  fi
done

3

우리는 한 번에 많은 변수를 확인하는 훌륭한 주장을 작성할 수 있습니다.

#
# assert if variables are set (to a non-empty string)
# if any variable is not set, exit 1 (when -f option is set) or return 1 otherwise
#
# Usage: assert_var_not_null [-f] variable ...
#
function assert_var_not_null() {
  local fatal var num_null=0
  [[ "$1" = "-f" ]] && { shift; fatal=1; }
  for var in "$@"; do
    [[ -z "${!var}" ]] &&
      printf '%s\n' "Variable '$var' not set" >&2 &&
      ((num_null++))
  done

  if ((num_null > 0)); then
    [[ "$fatal" ]] && exit 1
    return 1
  fi
  return 0
}

샘플 호출 :

one=1 two=2
assert_var_not_null one two
echo test 1: return_code=$?
assert_var_not_null one two three
echo test 2: return_code=$?
assert_var_not_null -f one two three
echo test 3: return_code=$? # this code shouldn't execute

산출:

test 1: return_code=0
Variable 'three' not set
test 2: return_code=1
Variable 'three' not set

여기에 더 많은 주장이 있습니다 : https://github.com/codeforester/base/blob/master/lib/assertions.sh



1

긴 프로세스를 시작하기 전에 설정 해야하는 개방형 변수 목록을 환경에서 확인하기 때문에 위의 솔루션 중 어느 것도 내 목적으로 효과가 없었습니다. 나는 이것으로 끝났다.

mapfile -t arr < variables.txt

EXITCODE=0

for i in "${arr[@]}"
do
   ISSET=$(env | grep ^${i}= | wc -l)
   if [ "${ISSET}" = "0" ];
   then
      EXITCODE=-1
      echo "ENV variable $i is required."
   fi
done

exit ${EXITCODE}

-1

외부 쉘 스크립트를 사용하는 대신 로그인 쉘에서 함수를로드하는 경향이 있습니다. 설정된 변수가 아닌 환경 변수를 확인하는 도우미 함수로 다음과 같은 것을 사용합니다.

is_this_an_env_variable ()
    local var="$1"
    if env |grep -q "^$var"; then
       return 0
    else
       return 1
    fi
 }

1
이것은 잘못이다. 존재 is_this_an_env_variable P하기 때문에 성공을 반환 PATH합니다.
Eric

환경 변수이기 때문에 존재합니다. 그것이 널이 아닌 문자열로 설정되어 있는지 여부는 또 다른 문제입니다.
nafdef

-7

$?구문은 멋지다 :

if [ $?BLAH == 1 ]; then 
    echo "Exists"; 
else 
    echo "Does not exist"; 
fi

이것은 효과가 있지만 여기서 누가 그 구문에 대한 문서를 찾을 수 있는지 알고 있습니까? $? 일반적으로 이전 반환 값입니다.
Danny Staple

내 나쁜-이것은 작동하지 않는 것 같습니다. 그 세트가 있거나없는 간단한 스크립트로 시도하십시오.
Danny Staple

11
Bourne에서 Korn, Bash 및 POSIX 쉘 $?은 이전 명령의 종료 상태입니다. $?BLAH'BLAH'에 연결된 이전 명령의 종료 상태로 구성된 문자열입니다. 이것은 변수 $BLAH를 전혀 테스트하지 않습니다 . (에 필요한 것을 어느 정도 할 수 있다는 소문이 csh있지만, 바다 조개에는 바다 조개가 가장 잘 남아 있습니다.)
Jonathan Leffler

이것은 Bash에 관한 한 완전히 잘못된 대답입니다.
codeforester
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.