複数の変数が設定されているかどうかをテストします


19

スクリプトの特定のポイントで、source構成ファイルを作成した後、いくつかの変数が設定され、設定されていない場合は実行を停止し、不足している変数についてユーザーに通知するようにします。私が試してみました

for var in $one $two $three ; do
    ...

ただし、たとえば$two設定されていない場合、ループは実行されません$two。次に試したのは

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

しかし、2が設定されていない場合、「2は設定されていません」ではなく、「2が設定されています」と表示されます。

必要な変数がすべて設定されていることを確認するにはどうすればよいですか?

更新/解決策:「set」と「set、but empty」に違いがあることを知っています。私は今(/programming//a/16753536/3456281とこの質問への回答のおかげで)以下を使用しています:

if [ -n "${!var:-}" ] ; then

そのため、varが設定されていても空の場合、それはまだ無効と見なされます。


1
またset -u、スクリプトの先頭に追加して、未設定の変数が使用されたときにスクリプトをすぐに終了することもできます。
n.st 14年

回答:


8

引用エラー。

if [ -n "${!var}" ] ; then

将来のために:設定

set -x

コードを実行する前に問題を示していました。それをコードに追加する代わりに、スクリプトを呼び出すことができます

bash -vx ./my/script.sh

これは機能しますが、引用符で何が起こっていますか?私の一般的なアプローチは最初の場所で正しいですか?
ジャスパー14年

ええ、それは予知的です。質問される前に質問に答えました... 8
ハウケレイジング14年

4
@Jasper 常に変数引用する必要があります。これは毎回これが必要かどうかを考えるよりも時間がかかりません。ただし、エラーは回避されます。
ホークレイジング14年

それどころか、引用は拡張に対してのみ保護します。拡張があなたの興味のあるものであるなら、引用は確かに進むべき道ではない 例えば:cs = 673,290,765、; セット-$(IFS =、; echo $ cs); echo $#; 出力:3
mikeserv 14年

2
@mikeserv単一の引用符のみが、私が明らかに示唆していないものを拡張から保護します。二重引用符は、単語の分割を防ぎます。引用を省略する必要がある場合があることを否定していません。しかし、それは私の一般的なルールに対する議論ではありません。どのような人が次のようなものを使用しますかset -- $(IFS=, ; echo $cs)?ここで尋ねなければif [ -n ${!var} ] ; thenならない種類の人々はなぜ機能しないのですか?おそらくない。
ホークレイジング14年

5

必要なのは、テスト内の引用符のみです。

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

私のために働く。


4

プログラムを停止する場合:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

これでうまくいきます。疑問符に続く各メッセージが出力されstderr、親シェルが終了します。わかりました。メッセージではなくメッセージが1つだけです。失敗した最初のメッセージだけでメッセージが出力され、シェルが終了します。私はこれらのテストを次のように使用します:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

ここでもっと多くのことを言いました


2

あなたは付け加えられます

set -u

設定されていない変数を使用しようとしたときにスクリプトを終了させるために、スクリプトの先頭まで。

のようなスクリプト

#!/bin/sh
set -u
echo $foo

になります

script.sh:3:sc​​ript.sh:foo:パラメーターが設定されていません

bash代わりに使用している場合、エラーは次のようになります。

script.sh:3行目:foo:バインドされていない変数


そして、あなたの意見では、これはランタイムチェックのためにどの理にかなっていますか?
Hauke Laging

2
@HaukeLaging私はあなたについてきちんとフォローしていません— set -uOPが回避しようとしている種類のエラーを正確に防ぎ、(他のすべてのソリューションとは異なり)特定の変数セットに限定されません。実際、変数が設定されていないときに予期しないことをする代わりに、ほとんどすべてのシェルスクリプトが安全に失敗するようにすることは、有用な予防策です。この記事は、このトピックに関する便利なリファレンスです。
n.st 14年

@ n.st私は同意しません-あなたがそれを計画している場合、nullはnullでないのと同じくらい有用な値になる可能性があります。
mikeserv 14年

1
@ n.stこれは、設定されていない変数へのアクセスに対する保護を望まないため、OPにとって意味がありません(BTW:設定されているが空の変数は同じエラーを引き起こしますが、「保護」に反応しません)。彼は、変数が設定されていない/空であるかどうかの実行時チェックを望んでいます。あなたの提案は一般的な開発の助けとして役立つかもしれませんが、OPの問題を解決しません。
ホークレイジング14年

set -u使用することもできると思いますが、適切なパラメーター拡張ほど柔軟ではありません(更新された質問を参照)。そしてset -u、1つの場所でそれらの存在を確認したい場合、すべてのターゲット変数へのダミーアクセスを作成する必要があります。
ジャスパー14年

2

最も友好的なソリューションは、すべての要件をテストし、それらを一緒に報告します。最初の要件で失敗し、物事を正しく行うために前後に要求するのではありません。

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

配列変数を使用して、設定されていない変数を追跡し、その結果を使用してユーザー向けのメッセージを作成します。

ノート:

  • 主に習慣からループで引用し${required_vars[@]}ましたfor-変数名にシェルのメタキャラクターを含めることはお勧めしません!
  • ${#missing_vars[@]}前のアドバイスを無視するほどひねくれていても、それは常に整数であるため、私は引用しませんでした。
  • %q印刷時に使用しました。%s通常は十分でしょう。
  • エラー出力は常にエラーストリーム>&2に送られるため、ダウンストリームコマンドにパイプされません
  • 沈黙は黄金色です-特に指示がない限り、進捗情報やデバッグ情報を出力しないでください。これにより、エラーがより明確になります。

1

bash4.2では、-v演算子で変数が設定されているかどうかをテストできます。設定されていない変数と空の文字列に設定された変数は、2つの異なる条件です。

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

私は...私は間接のようなものを探していたので、「複数」の変数について尋ねた
ジャスパー

あなたは以来、間接を必要としない-vかかる名前をので、変数のfor var in one two three; [[ -v $var ]] && ...; doneそれぞれのかどうかを確認だろうonetwoと、three順番に設定されています。
chepner 14年

1

というのであればnot set、変数は決して初期化されてはいけません。を使用すると[ -n "${!var}" ]、のような空の変数two=""は失敗しますが、設定されます。これを試すことができます:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

sourcedスクリプトで変数をnull値に設定しても問題ない場合に対処するための簡潔な(多少ハッキングな)ソリューションは、スクリプトをソースする前に変数を特定の値に初期化し、その後その値を確認することです。 、例えば

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
別の素敵なソリューションが、私はその後、「1つまたは複数の変数の設定を解除」、より具体的なエラーメッセージを与えるしたいのですが...
ジャスパー

0

@mikeservの答えを拡張しました。

このバージョンは、存在を確認する変数のリストを取り、不足している変数の名前を出力し、エラー時にusage()の呼び出しをトリガーします。

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

値が欠落している場合の出力例:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

VALUEという変数を、目的の出力により適した代替名に変更できることに注意してください。

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