17

シェルスクリプトで変数が空/未定義かどうかをテストする移植可能で標準的な方法は何ですか? sh に似たすべてのシェルで動作するはずです。私が今していることは次のようなものです:

if [ -z "$var" ] ; then
    ...

逆に、変数が空/未定義でない場合に何かを実行します。

if [ -n "$var" ] ; then
    ...

そして、これらは私が今書いているスクリプトで機能しますが、GNU userland とbashまたはdashを備えた Linux PC よりもあいまいな環境であっても、合理的に互換性のあるshのような shellで機能する方法を知りたいです。

「うまくいくはずだ」などの意見ではなく、さまざまなシェル環境での経験があり、さまざまなやり方の落とし穴を知っている人からの「専門家の回答」を探しています。

4

5 に答える 5

12

ブラケットの構造[...]は POSIX 標準の一部であり、どこにいても安全です。したがって、変数が空かどうかをテストするifには、質問で使用されているケースが最適です。

if [ -z "$STRING" ]; then
    # $STRING is empty
fi

あなたが覚えている限り:

常に変数を引用符で囲みます

引用符で囲まれていない変数は単語分割の対象となるため、[ -z $STRING ](引用符で囲まずに$STRING)を使用し$STRING、スペースが含まれている場合、または空である場合、シェルは[ -z SEVERAL WORDS ]or [ -z ]which が両方とも構文エラーであると見なします。つまり、必要なものではありません。

常に等号を 1 つだけ使用する

POSIX シェルの文字列が空かどうかをテストする別の方法は、 を使用すること=です。

if [ "$STRING" = "" ]; then
    # $STRING is empty
fi

ただし、内部で等号 () を二重に使用しないでください。(これは is 、またはがへのリンクである場合にのみ機能しますが、他の何かへのリンクがある別のマシンで実行する場合は機能しません。) 二重等号を使用する場合は、二重括弧も使用する必要があります(これはなどでのみ動作しますが、のような単純な POSIX シェルでは動作しません)。==[...]bashshbashsh[[...]]zshbashdash

使用しないでください&&||[...]

一部のシェルでは機能する可能性がありますが、すべてのシェルで機能するわけではありません。代わりに、' and[...] && [...] ' にはorと書き[... -a ...]ます。また、「または[...] || [...]」にはorと書き[... -o ...]ます。

古代のもの

非常に古いシェル スクリプトでは、空の変数をテストするために、次のようなものが表示されることがあります。これはまだ完全に有効です (しかし、私の目には不格好に見えます)、これを必要とするシェルに遭遇したことはありません (ただし、確かに、私の経験は Linux 以外では限られています。これがまだ必要な BSD シェルがあるのでしょうか?)

if [ ".$STRING" = "." ]; then
    # $STRING is empty
fi

ピリオドを先頭に追加するのは、やその他のオプションが含まれ[...]ている場合にブラケットの構成が混乱するのを避けるためです。ピリオドを前に付けることで、オプションなしになります。$STRING-e-o[...]

ただし、シェルがこの回避策を必要とするのを見たことがありません。(ダッシュは確かにそれらを必要としません。) したがって、これを無視してもおそらく安全です。

于 2014-07-23T00:41:20.283 に答える
6

免責事項: 私は自分自身を砲弾の専門家とは考えていません。

私の知る限り、testコマンドと-zフラグ-nは POSIX 標準の一部であるため、多くのプラットフォームで使用しても問題ありません。

于 2013-11-05T14:34:49.080 に答える
3

空のシェル変数は、設定されていないシェル変数と同じではないことを覚えておくことが重要です。ただし、この区別は、[ test ]. できることは、変数の展開の値を報告するtestことだけです - それを決定するのはパラメータの展開です。例えば:

var=value
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently not empty.

また:

var=
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently empty.

[ test ]:

{   _nstr() { echo 'I just evaluated a ""null string!' ; }
    [ -z "$var" ] && _nstr
    [ -z "" ] && _nstr
    unset var    
    [ -z "$var" ] && _nstr
    echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
}
###OUTPUT###
I just evaluated a ""null string!
I just evaluated a ""null string!
I just evaluated a ""null string!

うまくいけば、これで家に帰ります:

var=value
[ -z "${var+}" ] && _nstr
###OUTPUT###    
I just evaluated a ""null string!

変数が空かどうかをポータブルに判断したい場合は、次のようにします。

${var:+false} [ -n "${var+1}" ] && echo \$var is set and null

このようなケースでは、シェルのパラメーター拡張を非常にうまく利用できると思います。場合によっては、一石で二羽の鳥を殺すことさえできます (最初の例を参照)。これは間違いなく移植可能でなければなりません。以下は、ここにあるオープン グループの POSIX シェル ガイドラインからの不完全なコピー/貼り付けです

${parameter:-word}

  • デフォルト値を使用します。パラメータがunsetまたはの場合null、単語の展開が置換されます。それ以外の場合は、パラメーターの値が代入されます。

${parameter:=word}

  • デフォルト値を割り当てます。parameter がunsetまたはの場合null、word の展開が parameter に割り当てられます。いずれの場合も、パラメータの最終値が代入されます。この方法で割り当てることができるのは、位置パラメータや特殊パラメータではなく、変数のみです。

${parameter:?[word]}

  • Null または未設定の場合はエラーを示します。パラメータがunsetまたはの場合null、word の展開 (word が省略されている場合は設定されていないことを示すメッセージ) が標準エラーに書き込まれ、シェルはゼロ以外の終了ステータスで終了します。それ以外の場合は、パラメーターの値が代入されます。対話型シェルは終了する必要はありません。

${parameter:+word}

  • 代替値を使用します。パラメータがunsetまたはnullの場合は、null置換されます。それ以外の場合は、word の展開が代用されます。

例:

${parameter:-word}

  • この例では、がまたはlsの場合にのみ実行されます。(コマンド置換表記については、コマンド置換で説明しています。) xnullunset$( ls)${x:-$(ls)}

${parameter:=word}

% unset X
% echo ${X:=abc}
abc

編集:

アシッシュのワンライナーに気づいて、それを貼るだけでちょっと安っぽくなったので何とかしたい。したがって、ここに POSIX パラメータ展開の set-test ワンライナーがあります。

_JAIL="" ; echo "_JAIL is ${_JAIL:-unset or null.}"

まあ、それでも安いと思います。

EDIT2:

だから私はこれに気づきました:「...これらは私が今書いているスクリプトで機能しますが、Linuxよりもあいまいな環境であっても、合理的に互換性のあるshのようなシェルで機能する方法を知りたいですGNU ユーザーランドと bash または dash を備えた PC..."

申し訳ありませんが、私は個人的に専門家ではありませんが、かなり専門的に書かれた initramfs busybox スクリプトを掘り下げた後、渡された、または渡されなかった可能性のある引数から重要なシェル変数を定義する次の方法を最初に取り上げました。ほぼどこでも期待どおりに機能します。

ユーザーが変数を定義していない場合にのみ、変数をデフォルトの状態に設定するだけなので、実際に変数が定義されているかどうかを確認するための追加の作業は必要ありません。もちろん、他にもたくさんの用途がありますが、私はそれらを適切に使用できるほど頭がよくありません。

_VAR1=${1:-/default/path} ; _VAR2=${2:-"$_default_size"}

EDIT3:

phsは、質問で提起されたように、彼のメソッドが空の変数ではなく、空の変数をテストすることを指定したことは正しかった. このメソッドは、中央のコロンが使用されているかどうかに応じて同じことを行うことができます。これを実証するために、POSIX ガイドラインの下にある種のくだらない表をコピーしました。

{subs='substitute'; params='parameters'; assn='assign'; err='error'} 

         "$parameter" == |set && !null |set && null|  !set
       ------------------ ------------- ----------- ----------
      ${parameter:-word} | subs params | subs word | subs word
       ------------------ ------------- ----------- ----------
      ${parameter-word}  | subs params | subs null | subs word
       ------------------ ------------- ----------- ----------
      ${parameter:=word} | subs params | assn word | assn word
       ------------------ ------------- ----------- ----------
      ${parameter=word}  | subs params | subs null | assn word
       ------------------ ------------- ----------- ----------
      ${parameter:?word} | subs params | err; exit | err; exit
       ------------------ ------------- ----------- ----------
      ${parameter?word}  | subs params | subs null | err; exit
       ------------------ ------------- ----------- ----------
      ${parameter:+word} |  subs word  | subs null | subs null
       ------------------ ------------- ----------- ----------
      ${parameter+word}  |  subs word  | subs word | subs null
于 2013-11-20T00:27:25.240 に答える
0

変数のテストで大きなコードを実行していない限り、 if else はしごに入る必要はありません

ワンライナーの例

_JAIL=""
[  -z "$_JAIL" ] && echo "No" || echo "Yes"

他の例については、ここをクリックしてください。

于 2013-11-05T09:53:10.787 に答える