Unixシェルスクリプトで環境変数が設定されていることを確認する簡潔な方法は何ですか?


500

いくつかのUnixシェルスクリプトを持っているので、作業を開始する前に特定の環境変数が設定されていることを確認する必要があるので、次のようにします。

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

パラメータ拡張

明白な答えは、パラメーター展開の特殊な形式の1つを使用することです。

: ${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 = ""(空の文字列)は問題ありません。正確には望みどおりではなく、代わりの古い表記法です。

2番目のバリアント(を使用:?)では、DESTを設定し、空にする必要があります。

メッセージを指定しない場合、シェルはデフォルトのメッセージを提供します。

この${var?}構成は、バージョン7 UNIXおよびBourne Shell(1978年前後)に移植可能です。${var:?}私はそれが1981年ごろシステムIII UNIXでだったと思うが、それはその前のPWB UNIXにされていることがあります。構文は少し最近のことです。したがって、それはKornシェルと、特にBashを含むPOSIXシェルにあります。

通常は、シェルのマニュアルページの「パラメーター拡張」というセクションに記載されています。たとえば、bashマニュアルは言う:

${parameter:?word}

NullまたはUnsetの場合はエラーを表示します。パラメータがnullまたは未設定の場合、単語の展開(または単語が存在しない場合はその旨のメッセージ)が標準エラーに書き込まれ、対話型でない場合はシェルが終了します。それ以外の場合は、パラメーターの値が置き換えられます。

コロンコマンド

おそらく、colonコマンドは単に引数が評価されて成功することを付け加えておきます。これは、元のシェルコメント表記です( ' #'の前から行末まで)。長い間、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年以来、さまざまなバージョンのUnixを使用してきましたが、この構文を見たことはありません-表示されるだけです
AndrewR

これはどのように機能し、どこに文書化されていますか?変数が存在し、特定の値に設定されていることを確認するように変更できるかどうか疑問に思っています。
jhabbott

6
シェルのマニュアルページまたはBashのマニュアルに記載されており、通常は「パラメーター拡張」のヘッダーの下にあります。変数が存在し、特定の値(たとえば、「abc」)に設定されているかどうかを確認する標準的な方法は、次のとおりif [ "$variable" = "abc" ]; then : OK; else : variable is not set correctly; fiです。
ジョナサンレフラー

スペースを含む文字列変数でこの構文を使用するにはどうすればよいですか。チェックする変数を「これはテストです」に設定すると、「これ:コマンドが見つかりません」というエラーが表示されます。何か案は?
user2294382 2013年

1
この議論について何か考えはありますか?github.com/koalaman/shellcheck/issues/...
blong

138

これを試して:

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

8
それはかなり冗長です。セミコロンは冗長です。
ジョナサンレフラー

3
またはさらに短いこのブログ投稿を[ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; } 参照してください
zipizap '18

36
つまり、読み取り可能です。私はこれですべてです。Bashスクリプトには、その部門で得られるすべての支援が必要です。
Iain Duncan

元の答えの構文はより一貫しています。
速い歯

4
ただし、これは未設定の変数と空の文字列に設定された変数を区別しません。
chepner

57

質問は、使用しているシェルによって異なります。

ボーンシェルは、あなたが求めているものの邪魔になることはほとんどありません。

だが...

ほぼどこでも機能します。

cshに近づかないでください。ボーンシェルと比較して、追加したベルとホイッスルには良かったのですが、今は本当にきしんでいます。私を信じないなら、cshでSTDERRを分離してみてください!(-:

ここには2つの可能性があります。上記の例、つまり次を使用:

${MyVariable:=SomeDefault}

初めて$ MyVariableを参照する必要があります。これはenvを取ります。var MyVariable。現在設定されていない場合は、SomeDefaultの値を後で使用できるように変数に割り当てます。

次の可能性もあります。

${MyVariable:-SomeDefault}

これは、この構成を使用している変数をSomeDefaultに置き換えるだけです。変数にSomeDefault値を割り当てず、このステートメントが検出された後も、MyVariableの値はnullのままです。


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

Bourneシェルは必要なことを行います。
ジョナサンレフラー

51

確かに最も簡単な方法は-u、次のコードを使用していると仮定して、スイッチをシバン(スクリプトの上部にある行)に追加することですbash

#!/bin/sh -u

これにより、バインドされていない変数が内部に潜んでいる場合、スクリプトが終了します。


40
${MyVariable:=SomeDefault}

MyVariableが設定されていてnullでない場合、変数値がリセットされます(=何も起こりません)。
そうでMyVariableなければ、に設定されていますSomeDefaultます。

上記は実行しようとする${MyVariable}ので、変数を設定したいだけなら:

MyVariable=${MyVariable:=SomeDefault}

これはメッセージを生成しません-とQはデフォルトがないと言います(あなたが答えを入力したときにそれは言っていませんでした)。
ジョナサンレフラー

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:?}表記法などをサポートするを持っている
Jonathan Leffler

これまでのベストアンサー。
Ole

4
空の文字列に設定されることは、まったく設定されないこととは異なります。このテストでは、unset MYVARおよびの両方の後に「存在しない」と表示されMYVAR=ます。
chepner

@chepner、私はあなたのコメントをおそらく価値のあるヘッズアップに賛成しましたが、それから(bashを使用して)テストを続けましたが、実際にvarを空の文字列に設定することもできませんでした。どうしますか
Sz。

@Szどういう意味かわかりません。「未設定」とは、名前に値が割り当てられていないことを意味します。fooが設定さ"$foo"れていない場合でも、空の文字列に展開されます。
chepner 2018年

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

私のような将来の人々のために、私は一歩進んで変数名をパラメーター化したかったので、変数名の可変サイズのリストをループできます:

#!/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

それは環境変数だからです。eqaul nonnull stringが設定されているかどうかは、別の問題です。
nafdef 2018年

-7

$?構文はかなりきちんと次のとおりです。

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

これは機能しますが、その構文に関するドキュメントがどこにあるかを知っている人はいますか?$?通常は前の戻り値です。
ダニーステープル2010

私の悪い-これは機能していないようです。そのセットの有無にかかわらず、簡単なスクリプトで試してください。
Danny Staple 2010

11
Bourne、Korn、Bash、およびPOSIXシェルで$?は、前のコマンドの終了ステータスです。$?BLAH前のコマンドの終了ステータスを「BLAH」で連結した文字列です。これは変数$BLAHをまったくテストしません。(それは多かれ少なかれで必要とされるかもしれないという噂がありますが、csh貝殻は海岸に残されるのが最善です。)
ジョナサン・レフラー2013年

Bashに関する限り、これは完全に間違った答えです。
codeforester 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.