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


16

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

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

-vはのオプションではなく、test条件式の演算子です。
16

@Timトークン、文字列、および行の一部であるoptionことに加えて、コマンドtest -vへのoperatorto、aへのa conditional expressionおよびunary test primaryforの3つのことです[ ]。英語とシェル定義を混同しないでください。

回答:


36

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

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

空であるか定義されていないかに関係なく同じ方法で${foobar:+1}処理する場合は、このようにしますfoobar。が未定義の${foobar-}場合に空の文字列を取得し、それ以外の場合foobarは値を取得するために使用することもできfoobarます(またはの後に他のデフォルト値を配置します-)。

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

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

bashでは、配列は異なる驚くべき方法で動作します。空でない配列の場合に${a+1}のみ展開します。たとえば1a

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です。

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

ここで説明するすべての状況で、でエラーが発生する${foobar+1}場合にのみ空の文字列に展開することに注意してください。$foobarset -o nounset


1
配列はどうですか?Bashバージョン「GNU bash、バージョン4.1.10(4)-release(i686-pc-cygwin)」echo "${foobar:+1}"では1declare -a foobar以前に発行されたためfoobar、インデックス付き配列である場合は出力されません。declare -p foobar正しく報告しdeclare -a foobar='()'ます。い"${foobar:+1}"非配列変数のためだけの仕事を?
ティムフリスケ

「定義済み」の定義が「下で機能する」の場合、@ TimFriske ${foobar+1}:元の回答では、2つの例を逆にした)がbashの配列に対して正しいです。定義が異なる場合、bashは少し奇妙です。更新された回答をご覧ください。$foobarset -o nounset
ジル「SO-悪であるのをやめる」

1
「bashでは、配列は異なる驚くべき方法で動作します。」この動作は、bash(1)のマンページのセクション「配列」から説明できます。「添字なしで配列変数を参照することは、添字0で配列を参照することと同等です」と述べています。そのため、0インデックスもキーもtrueとして定義されていない場合a=()${a+1}正しく何も返しません。
ティムフリスケ

1
@TimFriske bashの実装がそのドキュメントに準拠していることを知っています。しかし、空の配列を未定義の変数のように扱うのは、本当に奇妙な設計です。
ジル「SO-悪であるのをやめる」

私はから結果を好む:変数がバッシュに設定されているかどうかを確認する方法は?-> 正しい方法 ...私は、これがどのように機能するべきかについて和解します。あえて言うが、Windowsにはdefined演算子があります。 Test それを行うことができます。それは(難しいことはできませんええと ......)
ます

5

ジルの答えをまとめると、次のルールを作りました。

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

3

私は、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

配列に複数の値がある場合、[@]を削除する必要がありました。
スタインインゲMorisbak

1
値が定義されているかどうかではなく、値があるかどうかをテストしたい限り、これはうまく機能します。foobar=""つまり、それを報告しfoobar is unsetます。待って、それを取り戻す。本当に最初の要素が空かどうかだけをテストするので、変数が配列ではないことを知っていて、空ではなく定義されていることだけを気にするのが良い考えのようです。
ロンバーク

未定義の変数を許可してスクリプトを実行している場合にのみ機能します(設定なし-u)
Florian Heigl
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.