web-dev-qa-db-ja.com

nounset Shellオプションを使用して、バージョン4.2より前のBashで変数がまったく定義されているかどうかをテストするにはどうすればよいですか?

「GNU bash、バージョン4.2」より前のBashバージョンの場合、testコマンドの-vオプションに相当する代替手段はありますか?例えば:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo
18
Tim Friske

すべてのPOSIXシェルに移植可能:

_if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi
_

空の場合も定義されていない場合もfoobarを同じように扱いたい場合は、_${foobar:+1}_にします。 _${foobar-}_を使用して、foobarが未定義の場合は空の文字列を取得し、それ以外の場合はfoobarの値を取得できます(または_-_の後に他のデフォルト値を配置します)。

Kshでは、foobarが宣言されているが定義されていない場合、_typeset -a foobar_のように、_${foobar+1}_は空の文字列に展開されます。

Zshには宣言されているが設定されていない変数はありません。_typeset -a foobar_は空の配列を作成します。

Bashでは、配列は別の驚くべき方法で動作します。 _${a+1}_は、aが空でない配列の場合にのみ_1_に展開されます。

_typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1
_

同じ原則が連想配列にも適用されます。配列変数は、空でないインデックスのセットがある場合、定義されたものとして扱われます。

任意のタイプの変数が定義されているかどうかをテストする別のbash固有の方法は、_${!PREFIX*}_にリストされているかどうかを確認することです。これは、_${foobar+1}_とは異なり、定義された空の配列を報告しますが、宣言されたが割り当てられていない変数(_unset foobar; typeset -a foobar_)を未定義として報告します。

_case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac
_

これは _typeset -p foobar_または_declare -p foobar_ の戻り値をテストすることと同等ですが、宣言されたが割り当てられていない変数で_typeset -p foobar_が失敗する点が異なります。

Bshでは、kshと同様に、_set -o nounset; typeset -a foobar; echo $foobar_は、未定義の変数foobarを展開しようとしてエラーをトリガーします。 kshとは異なり、set -o nounset; foobar=(); echo $foobar(または_echo "${foobar[@]}"_)もエラーをトリガーします。

ここで説明するすべての状況で、_${foobar+1}_が_$foobar_でエラーを引き起こす場合に限り、_set -o nounset_は空の文字列に展開されます。

Gillesの回答を要約すると、次のルールを作成しました。

  1. 使用する [[ -v foobar ]] Bashバージョン4.2以降の変数の場合。
  2. 使用する declare -p foobar &>/dev/null Bashバージョン<4.2の配列変数。
  3. 使用する (( ${foo[0]+1} ))または(( ${bar[foo]+1} ))インデックス付きの下付き文字(-a)およびキー付き(-A)配列(declare)それぞれ。オプション1と2はここでは機能しません。
6
Tim Friske

私はbashのすべての変数に同じテクニックを使用し、それは機能します、例えば:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

出力:

foobar is unset

ながら

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

出力:

foobar is set
3