問題の簡単な説明:
配列の名前が動的(変数に格納されているなど)のbash配列の要素数を数える組み込みbashメソッドはありますか?いいえ配列の完全なコピーを作成するかeval
?
追加情報:
Bashパラメーター置換を使用すると、次のことができます。
- 配列の長さを決定します
myArr=(A B C); echo ${#myArr[@]}
。 - 名前で変数を間接的に参照: (
NAME=myVar; echo ${!NAME}
これは配列要素にも適用されます):
NAME=myArr[1]; echo ${!NAME}
しかし、配列の名前が別の変数に格納されている場合、配列の要素数をどのように決定しますか? (誰かがこれだと思うかもしれません。コンビネーション上記の2つのパラメータを変更してください。 )例えば:
myArr=(A B C D)
NAME=myArr
# Get the number of elements in the array indirectly referenced by NAME.
count=${#$NAME[@]} # This syntax is invalid. What is the right way?
すべて失敗したいくつかの試みは次のとおりです。
# Setup for following attempts:
myArr=(A B C D)
NAME=myArr
EXPR1=$NAME[@] # i.e. EXPR1='myArr[@]'
EXPR2=#$NAME[@] # i.e. EXPR2='#myArr[@]'
# Failed attempts to get the lengh of the array indirectly:
1. count=${#$NAME[@]} # ERROR: bash: ...: bad substitution
2. count=${#!EXPR1} # ERROR: bash: !EXPR}: event not found
3. count=${#\!EXPR1} # ERROR: bash: ...: bad substitution
4. count=${!#EXPR1} # ERROR: bash: ...: bad substitution
5. count=${!EXPR2} # Returns NULL
また、上記の他のバリエーションも試しましたが、(A)配列のコピーを作成するか(B)eval
。
作業方法:
最適でない可能性があるこの問題を解決する方法はいくつかあります(ただし、間違っている場合は訂正してください)。
方法 1: 配列のコピー
配列を別の(静的に名前付き)変数に割り当て、その中にある要素の数を取得します。
EXPR=$NAME[@]
arrCopy=( "${!EXPR}" )
count=${#arrCopy}
方法2:使用eval
EXPR="count=\${#$NAME[@]}" # i.e. 'count=${myArr[@]}'
eval $EXPR
# Now count is set to the length of the array
要約:
配列の長さを間接的に決定するためにbashに組み込まれたメソッド(パラメータの代替構文など)はありますか?そうでなければ、最も効率的な方法は何ですか?私はこれがeval
上記のアプローチであると仮定していますが、セキュリティやパフォーマンスの問題はありますかeval
?
答え1
索引評価でこの項目を処理する必要があります。間接的にできる渡す配列で作成した場合は、間接変数のインデックスです。
a=(abc1 def2 ghi3 jkl4 mno5)
r=('a[c=${#a[@]}]' a\[i] a\[@])
for i in 0 1 2 3 4 5
do c=
printf "<%s>\n" "${!r-${!r[i<c?1:2]}}"
printf "\n\tindex is $i and count is $c\n\n"
done
<abc1>
index is 0 and count is 5
<def2>
index is 1 and count is 5
<ghi3>
index is 2 and count is 5
<jkl4>
index is 3 and count is 5
<mno5>
index is 4 and count is 5
<abc1>
<def2>
<ghi3>
<jkl4>
<mno5>
index is 5 and count is 5
インデックス付けは0から始まるので、bash
配列オブジェクトの総数は常に最も高いセットインデックスより1が多いです。したがって、次のようになります。
c=
echo "${a[c=${#a[@]}]-this index is unset}" "$c"
this index is unset 5
...引数が指定されると、引数はデフォルトの単語に展開されます。
提供されていない場合:
c=
${!r}
echo "$c"
5
…何の被害も負けませんでした。
ループ内でインデックス変数を追跡し$i
、その変数が少なくともカウントと同じであることを$c
確認します。より小さいときは有効なインデックスなので ef を拡張しますが$r
、a[i]
それ以上であれば$r
ef を全配列$a
に拡張します。
これは関数にあります:
ref_arr(){
local index=-1 count=
local ref=( "$1[ count= \${#$1[@]} ]"
"$1[ index ]" "$1[ @ ]"
) && printf "input array '%s' has '%d' members.\n" \
"$1" "${!ref-${count:?invalid array name: "'$1'"}}"
while [ "$((index+=1))" -lt "$count" ]
do printf "$1[$index] == '%s'\n" "${!ref[1]}"
done
}
some_array=(some "dumb
stuff" 12345\'67890 "" \
'$(kill my computer)')
ref_arr some_array
ref_arr '$(echo won'\''t work)'
input array 'some_array' has '5' members.
some_array[0] == 'some'
some_array[1] == 'dumb
stuff'
some_array[2] == '12345'67890'
some_array[3] == ''
some_array[4] == '$(kill my computer)'
bash: count: invalid array name: '$(echo won't work)'
答え2
bash 4.3 namerefは神の贈り物です。しかし、次のようにすることができます。
$ myArr=(A B C D)
$ NAME=myArr
$ tmp="${NAME}[@]"
$ copy=( "${!tmp}" )
$ echo "${#copy[@]}"
4