110

Using:

set -o nounset
  1. Having an indexed array like:

    myArray=( "red" "black" "blue" )
    

    What is the shortest way to check if element 1 is set?
    I sometimes use the following:

    test "${#myArray[@]}" -gt "1" && echo "1 exists" || echo "1 doesn't exist"
    

    I would like to know if there's a preferred one.

  2. How to deal with non-consecutive indexes?

    myArray=()
    myArray[12]="red"
    myArray[51]="black"
    myArray[129]="blue"
    

    How to quick check that 51 is already set for example?

  3. How to deal with associative arrays?

    declare -A myArray
    myArray["key1"]="red"
    myArray["key2"]="black"
    myArray["key3"]="blue"
    

    How to quick check that key2 is already used for example?

4

10 に答える 10

151

要素が設定されているかどうかを確認するには(インデックス付き配列と連想配列の両方に適用されます)

[ "${array[key]+abc}" ] && echo "exists"

基本的に何を${array[key]+abc}しますか

  • array[key]が設定されている場合は、abc
  • が設定されていない場合array[key]は、何も返しません

参照:
  1. Bashマニュアルのパラメータ拡張と小さなメモを参照してください

コロンが省略されている場合、オペレーターは[パラメーターの存在]についてのみテストします。

  1. この回答は、実際にはこのSOの質問に対する回答を基にしています。文字列がbashシェルスクリプトで定義されていないかどうかを確認するにはどうすればよいですか?

ラッパー関数:

exists(){
  if [ "$2" != in ]; then
    echo "Incorrect usage."
    echo "Correct usage: exists {key} in {array}"
    return
  fi   
  eval '[ ${'$3'[$1]+muahaha} ]'  
}

例えば

if ! exists key in array; then echo "No such array element"; fi 
于 2012-11-04T18:25:07.793 に答える
49

man bashから、条件式:

-v varname
              True if the shell variable varname is set (has been assigned a value).

例:

declare -A foo
foo[bar]="this is bar"
foo[baz]=""
if [[ -v "foo[bar]" ]] ; then
  echo "foo[bar] is set"
fi
if [[ -v "foo[baz]" ]] ; then
  echo "foo[baz] is set"
fi
if [[ -v "foo[quux]" ]] ; then
  echo "foo[quux] is set"
fi

これは、foo[bar]とfoo[baz]の両方が設定されており(後者は空の値に設定されている場合でも)、foo[quux]は設定されていないことを示しています。

于 2017-07-29T05:00:27.120 に答える
22

新しい答え

のバージョン4.2 (およびそれ以降)から、-v組み込み testコマンドの新しいオプションがあります。

バージョン4.3以降、このテストは配列の要素に対応できるようになりました。

array=([12]="red" [51]="black" [129]="blue")

for i in 10 12 30 {50..52} {128..131};do
    if [ -v 'array[i]' ];then
        echo "Variable 'array[$i]' is defined"
    else
        echo "Variable 'array[$i]' not exist"
    fi
done
Variable 'array[10]' not exist
Variable 'array[12]' is defined
Variable 'array[30]' not exist
Variable 'array[50]' not exist
Variable 'array[51]' is defined
Variable 'array[52]' not exist
Variable 'array[128]' not exist
Variable 'array[129]' is defined
Variable 'array[130]' not exist
Variable 'array[131]' not exist

注:sscのコメントに関しては、 shellcheckのエラーSC2208を満たすために、テストで重引用符で囲んでいます。とにかく、グロブ文字がないので、これはここでは実際には必要ないようです...'array[i]'-v array[i]

これは、連想配列でも同じように機能します。

declare -A aArray=([foo]="bar" [bar]="baz" [baz]=$'Hello world\041')

for i in alpha bar baz dummy foo test;do
    if [ -v 'aArray[$i]' ];then
        echo "Variable 'aArray[$i]' is defined"
    else
        echo "Variable 'aArray[$i]' not exist"
    fi
done
Variable 'aArray[alpha]' not exist
Variable 'aArray[bar]' is defined
Variable 'aArray[baz]' is defined
Variable 'aArray[dummy]' not exist
Variable 'aArray[foo]' is defined
Variable 'aArray[test]' not exist

少し違いがあります
。通常の配列では、角かっこ([i])間の変数は整数であるため、ドル記号($)は必要ありませんが、連想配列の場合、キーは単語で$あるため、必要です([$i])!

V4.2より前のに対する古い答え

残念ながら、bashはの変数と未定義の変数を区別する方法を提供しません。

しかし、いくつかの方法があります。

$ array=()
$ array[12]="red"
$ array[51]="black"
$ array[129]="blue"

$ echo ${array[@]}
red black blue

$ echo ${!array[@]}
12 51 129

$ echo "${#array[@]}"
3

$ printf "%s\n" ${!array[@]}|grep -q ^51$ && echo 51 exist
51 exist

$ printf "%s\n" ${!array[@]}|grep -q ^52$ && echo 52 exist

(答えない)

連想配列の場合、同じものを使用できます。

$ unset array
$ declare -A array
$ array["key1"]="red"
$ array["key2"]="black"
$ array["key3"]="blue"
$ echo ${array[@]}
blue black red

$ echo ${!array[@]}
key3 key2 key1

$ echo ${#array[@]}
3

$ set | grep ^array=
array=([key3]="blue" [key2]="black" [key1]="red" )

$ printf "%s\n" ${!array[@]}|grep -q ^key2$ && echo key2 exist || echo key2 not exist
key2 exist

$ printf "%s\n" ${!array[@]}|grep -q ^key5$ && echo key5 exist || echo key5 not exist
key5 not exist

外部ツール(純粋なbashのようにprintf | grepはありません)を必要とせずに作業を行うことができます。その場合は、 checkIfExist()を新しいbash関数としてビルドします。

$ checkIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) return 0 ;;
        * ) return 1 ;;
      esac";
}

$ checkIfExist array key2 && echo exist || echo don\'t
exist

$ checkIfExist array key5 && echo exist || echo don\'t
don't

または、新しいgetIfExist bash関数を作成して、目的の値を返し、目的の値が存在しない場合はfalseの結果コードで終了します。

$ getIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) echo \${$1[$2]};return 0 ;;
        * ) return 1 ;;
      esac";
}

$ getIfExist array key1
red
$ echo $?
0

$ # now with an empty defined value
$ array["key4"]=""
$ getIfExist array key4

$ echo $?
0
$ getIfExist array key5
$ echo $?
1
于 2012-11-04T15:15:32.250 に答える
11

-nテストと:-オペレーターはどうですか?

たとえば、次のスクリプトは次のとおりです。

#!/usr/bin/env bash

set -e
set -u

declare -A sample

sample["ABC"]=2
sample["DEF"]=3

if [[ -n "${sample['ABC']:-}" ]]; then
  echo "ABC is set"
fi

if [[ -n "${sample['DEF']:-}" ]]; then
  echo "DEF is set"
fi

if [[ -n "${sample['GHI']:-}" ]]; then
  echo "GHI is set"
fi

プリント:

ABC is set
DEF is set
于 2019-07-01T19:25:47.007 に答える
5

bash 4.3.39(1)でテスト済み-リリース

declare -A fmap
fmap['foo']="boo"

key='foo'
# should echo foo is set to 'boo'
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
key='blah'
# should echo blah is unset in fmap
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
于 2015-11-13T03:28:48.937 に答える
2

Thammeからこれを繰り返します:

[[ ${array[key]+Y} ]] && echo Y || echo N

これは、変数/配列要素が存在するかどうかをテストします。これには、null値に設定されているかどうかも含まれます。これは、-vよりも広範囲のbashバージョンで機能し、set-uなどの影響を受けないように見えます。このメソッドを使用して「不正な配列の添え字」が表示された場合は、例を投稿してください。

于 2021-02-23T12:37:02.240 に答える
1

これは私がスクリプトで見つけた最も簡単な方法です。

<search>は検索する文字列ASSOC_ARRAYであり、連想配列を保持する変数の名前です。

達成したいことに依存します:

キーが存在します

if grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key is present; fi

キーが存在しません

if ! grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key not present; fi

値が存在します

if grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value is present; fi

値が存在しません

if ! grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value not present; fi
于 2016-08-15T14:58:36.537 に答える
1

Bashの配列にキーが存在するかどうかをチェックする関数を作成しました。

# Check if array key exists
# Usage: array_key_exists $array_name $key
# Returns: 0 = key exists, 1 = key does NOT exist
function array_key_exists() {
    local _array_name="$1"
    local _key="$2"
    local _cmd='echo ${!'$_array_name'[@]}'
    local _array_keys=($(eval $_cmd))
    local _key_exists=$(echo " ${_array_keys[@]} " | grep " $_key " &>/dev/null; echo $?)
    [[ "$_key_exists" = "0" ]] && return 0 || return 1
}

declare -A my_array
my_array['foo']="bar"

if [[ "$(array_key_exists 'my_array' 'foo'; echo $?)" = "0" ]]; then
    echo "OK"
else
    echo "ERROR"
fi

GNU bash、バージョン4.1.5(1)-リリース(i486-pc-linux-gnu)でテスト済み

于 2017-02-22T10:45:34.070 に答える
1

すべての時間の人々のために、一度だけ。

長い道のりには「クリーンなコード」があり、より短く、より簡潔な、bash中心の道があります。

$1=探しているインデックスまたはキー。

$2=参照によって渡された配列/マップ。

function hasKey ()
{
    local -r needle="${1:?}"
    local -nr haystack=${2:?}

    for key in "${!haystack[@]}"; do
        if [[ $key == $needle ]] ;
            return 0
        fi
    done

    return 1
}

線形検索は、バイナリ検索に置き換えることができます。これにより、データセットが大きいほどパフォーマンスが向上します。最初にキーを数えて並べ替えてから、答えに近づくにつれて、干し草の山の古典的なバイナリ半分を実行します。

さて、「いいえ、bashで大きな配列を処理する必要があるかもしれないので、よりパフォーマンスの高いバージョンが必要です」のような純粋主義者のために、よりbash中心のソリューションを見てみましょう。ただし、クリーンなコードと柔軟性を維持するソリューションです。配列またはマップを処理します。

function hasKey ()
{
    local -r needle="${1:?}"
    local -nr haystack=${2:?}

    [ -n ${haystack["$needle"]+found} ]
}

この行では、キーの値もテストしようとする形式ではなく、bash変数展開[ -n ${haystack["$needle"]+found} ]の形式を使用していますが、これは当面の問題ではありません${parameter+word}${parameter:+word}

使用法

local -A person=(firstname Anthony lastname Rutledge)

if hasMapKey "firstname" person; then
     # Do something
fi

以下に説明する形式(例:':-')を使用して部分文字列展開を実行しない場合、Bashは未設定またはnullのパラメーターをテストします。コロンを省略すると、設定されていないパラメーターに対してのみテストが行​​われます。言い換えると、コロンが含まれている場合、オペレーターはパラメーターの存在とその値がnullでないことの両方をテストします。コロンが省略されている場合、オペレーターは存在についてのみテストします。

$ {パラメータ:-単語}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

$ {parameter:= word}

If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional

この方法では、パラメータと特別なパラメータを割り当てることはできません。$ {parameter:?word}

If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard

エラーが発生し、シェルが対話型でない場合は終了します。それ以外の場合は、パラメーターの値が置き換えられます。$ {parameter:+ word}

If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion

存在しない場合$needleは何も展開せず、存在しない場合は長さがゼロ以外の文字列「found」に展開します。これにより、実際に存在する-n場合はテストが成功し(「見つかった」と言います)、そうでない場合は失敗します。$needle

于 2021-08-08T14:11:39.317 に答える
0

bad array subscriptチェックしているキーが設定されていないとエラーになります。そこで、キーをループする関数を作成しました。

#!/usr/bin/env bash
declare -A helpList 

function get_help(){
    target="$1"

    for key in "${!helpList[@]}";do
        if [[ "$key" == "$target" ]];then
            echo "${helpList["$target"]}"
            return;
        fi
    done
}

targetValue="$(get_help command_name)"
if [[ -z "$targetvalue" ]];then
    echo "command_name is not set"
fi

見つかった場合は値をエコーし​​、見つからなかった場合は何もエコーしません。私が試した他のすべての解決策は私にそのエラーを与えました。

于 2021-02-09T17:41:19.697 に答える