Bashで配列が空かどうかを確認します


110

スクリプトを実行すると、さまざまなエラーメッセージでいっぱいになる配列があります。

スクリプトの最後にないかどうかを確認し、ある場合は特定のアクションを実行する方法が必要です。

私はすでに通常のVARのようにそれを扱い、それをチェックするために-zを使用してみましたが、それはうまくいかないようです。Bashで配列が空かどうかを確認する方法はありますか?

回答:


143

配列が$errorsであると仮定すると、要素の数がゼロかどうかを確認するだけです。

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

10
これ=は文字列演算子であることに注意してください。この場合はたまたまうまく動作しますが、-eq代わりに適切な算術演算子を使用します(-geまたはに切り替えたい場合-ltなど)。
ムシフィル

6
set -u「非バインド変数」では使用できません-配列が空の場合。
イゴール

@Igor:Bash 4.4で動作します。set -u; foo=(); [ ${#foo[@]} -eq 0 ] && echo empty。Iの場合、がunset foo出力されますがfoo: unbound variable、それは異なります。配列変数は、存在して空ではなく、まったく存在しません。
ピーターコーデス

また、使用時にBash 3.2(OSX)でテストされset -uています-最初に変数を宣言している限り、これは完全に機能します。
zeroimpl

15

この場合、通常は算術展開を使用します。

if (( ${#a[@]} )); then
    echo not empty
fi

素敵できれい!私はそれが好きです。また、配列の最初の要素が常に空でない場合(( ${#a} ))(最初の要素の長さ)も機能することに注意してください。しかし、それは上で失敗するa=('')のに対し、(( ${#a[@]} ))答えに与えられた成功します。
cxw

8

配列を単純な変数とみなすこともできます。そのように、ちょうど使用

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

または反対側を使用して

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

このソリューションの問題は、配列が次のように宣言されている場合ですarray=('' foo)。これらのチェックは、配列を空として報告しますが、明らかにそうではありません。(@musiphilに感謝!)

使用[ -z "$array[@]" ]することも明らかに解決策ではありません。中括弧を指定しない$arrayと、文字列([@]その場合は単純なリテラル文字列)として解釈しようとするため、常に「リテラル文字列は[@]空ですか?」と報告されます。明らかにない。


7
[ -z "$array" ]または[ -n "$array" ]動作しません。試してみてください。明らかに空ではありませんarray=('' foo); [ -z "$array" ] && echo emptyが、印刷emptyされますarray
ムシフィル

2
[[ -n "${array[*]}" ]]配列全体を文字列として補間し、長さがゼロでないことを確認します。array=("" "")空の要素を2つ持つのではなく、空であると考える場合、これは便利です。
ピーター・コーデス

@PeterCordesうまくいかないと思う。式は単一のスペース文字に評価され、[[ -n " " ]]「true」であり、残念です。あなたのコメントはまさに私がやりたいことです。
マイケル

@マイケル:クラップ、あなたは正しい。空の文字列の1要素配列でのみ機能し、2要素では機能しません。古いbashもチェックしましたが、まだ間違っています。あなたが言うset -xように、それがどのように拡大するかを示しています。投稿する前にそのコメントをテストしなかったと思います。>。< 展開するとIFSの最初の文字で要素が区切られるIFS=''ため、設定(このステートメントの前後に保存/復元)することで機能させることができ"${array[*]}"ます。(または未設定の場合はスペース)。しかし、「IFSがnullの場合、パラメーターはセパレーターを介さずに結合されます。」($ *位置パラメーターのドキュメント、配列についても同じと想定しています)。
ピーターコーデス

@Michael:これを行う回答を追加しました。
ピーターコーデス

3

私はそれをチェックしましたbash-4.4.0

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

およびbash-4.1.5

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

後者の場合、次の構成が必要です。

${array[@]:+${array[@]}}

空の配列または未設定の配列で失敗しないようにします。それはあなたがset -eu私が通常するように行う場合です。これにより、より厳密なエラーチェックが提供されます。ドキュメントから:

-e

単一の単純なコマンド(単純なコマンドを参照)、リスト(リストを参照)、または複合コマンド(複合コマンドを参照)で構成されるパイプライン(パイプラインを参照)がゼロ以外のステータスを返す場合、すぐに終了します。失敗したコマンドがwhileまたはuntilキーワードの直後のコマンドリストの一部、ifステートメントのテストの一部、&&または||で実行されたコマンドの一部である場合、シェルは終了しません。最後の&&または||に続くコマンド、パイプライン内の最後のコマンド以外のコマンド、またはコマンドの戻りステータスが!で反転されている場合を除きます。-eが無視されている間にコマンドが失敗したためにサブシェル以外の複合コマンドがゼロ以外のステータスを返す場合、シェルは終了しません。ERRのトラップは、設定されている場合、シェルが終了する前に実行されます。

このオプションは、シェル環境と各サブシェル環境に個別に適用され(コマンド実行環境を参照)、サブシェル内のすべてのコマンドを実行する前にサブシェルを終了させる場合があります。

複合コマンドまたはシェル関数が-eが無視されるコンテキストで実行される場合、複合コマンドまたは関数本体内で実行されるコマンドは、-eが設定され、コマンドがaを返す場合でも、-e設定の影響を受けません。障害ステータス。-eが無視されるコンテキストで実行中に複合コマンドまたはシェル関数が-eを設定した場合、その設定は複合コマンドまたは関数呼び出しを含むコマンドが完了するまで効果がありません。

-u

パラメーターの展開を実行するときに、特別なパラメーター「@」または「*」以外の未設定の変数とパラメーターをエラーとして扱います。エラーメッセージが標準エラーに書き込まれ、非対話型シェルが終了します。

必要ない場合は、:+${array[@]}一部を省略してください。

また、[[ここで演算子を使用することが不可欠であることに注意して[ください:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

では-u、あなたは、実際に使用すべき${array[@]+"${array[@]}"}CF stackoverflow.com/a/34361807/1237617
ヤクブBochenski

@JakubBochenski bashのどのバージョンについて話しているのですか?gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9
x-yuri

単一括弧の例の問題は@、確かにです。の*ような配列展開を使用[ "${array[*]}" ]できますか?それでも、[[正常に動作します。複数の空の文字列を持つ配列に対するこれらの両方の動作は、少し驚くべきものです。両方[ ${#array[*]} ][[ "${array[@]}" ]]のために偽であるarray=()array=('')のためではなく、真array=('' '')(二つ以上の空の文字列)。1つ以上の空の文字列をすべてtrueにしたい場合は、を使用できます[ ${#array[@]} -gt 0 ]。それらをすべて偽りにしたい場合は、//それらを除外することができます。
18

@eisdを使用できますが[ "${array[*]}" ]、そのような表現に遭遇した場合、それが何をするのか理解するのが難しくなります。[...]補間の結果の文字列の観点から動作するため。とは対照的に[[...]]、補間されたものを認識することができます。つまり、配列が渡されたことを知ることができます。[[ ${array[@]} ]]「配列が空でないかどうかを確認する」と読み、「[ "${array[*]}" ]すべての配列要素の補間の結果が空でない文字列であるかどうかを確認する」と読みます。
x-yuri

... 2つの空の文字列を使用した動作については、まったく驚くことではありません。驚くべきことは、1つの空の文字列での動作です。しかし、おそらく合理的です。に関して[ ${#array[*]} ][ "${array[*]}" ]、前者は任意の数の要素に当てはまるため、おそらくを意味します。要素の数は常に空でない文字列であるためです。2つの要素を持つ後者に関しては、括弧内の式は、に展開' '非空の文字列です。については[[ ${array[@]} ]]、2つの要素の配列は空ではないと考えています(そして当然のことです)。
x-yuri

2

空のように空の要素を持つ配列を検出したい場合はarr=("" "")arr=()

すべての要素を貼り付けて、結果の長さがゼロかどうかを確認できます。(配列の内容の平坦化されたコピーを構築することは、配列が非常に大きくなる可能性がある場合、パフォーマンスにとって理想的ではありません。

しかし"${arr[*]}"、の最初の文字で区切られた要素で展開しますIFS。したがって、IFSを保存/復元IFS=''してこの作業を行うか、文字列の長さ==#配列要素の数-1を確認する必要があります(n要素の配列にはn-1セパレータがあります)。それを1つずつ処理するには、連結を1ずつパディングするのが最も簡単です。

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

テストケース set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

残念ながらこれは以下で失敗しarr=()ます:[[ 1 -ne 0 ]] そのため、最初に個別に実際に空の配列を確認する必要があります。


またはでIFS=''。サブシェルから簡単に結果を取得できないため、サブシェルを使用する代わりに、IFSを保存/復元したいでしょう。

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

例:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

で作業arr=()-それはまだちょうど空の文字列です。


私は賛成しましたが[[ "${arr[*]}" = *[![:space:]]* ]]、少なくとも1つの非WSキャラクターに頼ることができるので、を使い始めました。
マイケル

@Michael:はい、拒否する必要がない場合は良いオプションですarr=(" ")
ピーターコーデス

0

私の場合、空白が存在する可能性があるため、2番目の回答では不十分でした。私は一緒に来ました:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

echo | wcシェルのビルトインを使用した場合と比較して、不必要に効率が悪いようです。
ピーターコーデス

@PeterCordesを理解しているかどうかわかりませんが、2番目の回答を修正して[ ${#errors[@]} -eq 0 ];、空白の問題を回避できますか?私も組み込みを好むだろう。
ミカ

空白はどのくらい正確に問題を引き起こしますか? $#は数に展開され、の後でも正常に動作しopts+=("")ます。例えばunset opts; opts+=("");opts+=(" "); echo "${#opts[@]}"、私は得る2。動作しないものの例を示してもらえますか?
ピーターコーデス

昔です。発信元のIIRCは常に少なくとも ""を出力します。したがって、opts = ""またはopts =( "")の場合、空の改行または空の文字列を無視して、1ではなく0が必要でした。
ミカ

わかりましたのでopts=("")、同じように扱う必要がありopts=()ますか?これは空の配列ではありませんが、を使用して空の配列または空の最初の要素を確認できますopts=(""); [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty。あなたの現在の答えはのために「オプションなし」と言っていることに注意してくださいopts=("" "-foo")すべての配列要素をゼロ以外の長さチェックするフラットな文字列に補間する[[ -z "${opts[*]}" ]]と推測でき-zます。 最初の要素をチェックするだけで十分な場合、-z "$opts"動作します。
ピーターコーデス

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