配列名の一部として変数を使用する方法


11

2つの配列があります。

arrayA=(1 2 3)
arrayB=(a b c)

そして、コマンドライン引数を使用して、つまりなしでそれらの1つを出力したいと思いますif else

構文のバリエーションをいくつか試しましたが、成功しませんでした。私はこのようなことをしたいと思っています:

ARG="$1"

echo ${array${ARG}[@]}

しかし、「悪い置換」エラーが発生します。どうすればこれを達成できますか?


これは、明らかに慣用的なbashではありません。これを行わないでください。
ワイルドカード

回答:


22

これを試してください:

$ arrayA=(1 2 3)
$ x=A
$ var=array$x[@]
$ echo ${!var}
1 2 3

注意

  • man bash(パラメータ展開):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

数字、またはパラメータの後にその名前の一部として解釈されない文字が続く場合。
*パラメータの最初の文字が感嘆符(!)の場合、変数の間接参照のレベルが導入されます。Bashは、残りのパラメーターから形成された変数の値を変数の名前として使用します。次に、この変数が展開され、その値がパラメーター自体の値ではなく、残りの置換で使用されます。これは間接展開と呼ばれます。*これの例外は、以下で説明する$ {!prefix *}および$ {!name [@]}の拡張です。感嘆符は、間接参照を導入するために、左中括弧の直後に続く必要があります。


変数の!前で何をしているのvarですか?それがどのように機能するか、それはグーグルでの歴史の代用であるように見えました、しかし私はそれがここでどのように機能するか見ることができませんでした。
アーロン

私の編集した投稿を参照してください
Gilles Quenot 2013年

4

別の回答で指摘されているように間接アクセスを使用できますが、別の方法(kshおよびBash 4.3以降)ではnamerefを使用します。特に配列の場合、namerefを介して配列にインデックスを付けることができ、参照として使用される変数にインデックスを置く必要がないため、これはより便利です。

arr1=(a b c)
arr2=(x y z)
typeset -n p=arr1    # or 'declare -n' 
echo "${p[1]}"       # prints 'b'

これは間接的なアクセスでは機能しません。

q=arr2
echo "${!q}"         # prints 'x', the same as $arr2
echo "${!q[1]}"      # doesn't work, it tries to take q[1] as a reference

Cプログラマーが言うように、${!q[1]}ここでqは、配列へのポインターではなく、ポインターの配列であるかのように動作します。



1
arrayA=(1 2 3)
arrayB=(a b c)

ARG="$1"

eval echo \${array${ARG}[@]}

dataget (){ 
    eval echo \${array${1}[${2:-@}]}
}
$ dataget A
1 2 3
$ dataget A 0
1
$ dataget B 1
b

注: スペースがある場合は、エスケープコート

eval dostuff \"\${array${1}[${2:-@}]}\"

1

これには多くの試行錯誤が必要でしたが、最終的には機能しました。

Younessからインスピレーションを受けました。しかし、他のすべての答えは私の古いbashを助けませんでした(suse11sp1 [3.2.51(1)-release])

'for'ループが間接配列の拡張を拒否しました。代わりに、それを事前拡張する必要があります。これを使用して、新しい変数名で別の配列を作成します。以下の例は、二重ループを示しています。

THEBIGLOOP=(New_FOO New_BAR)

FOOthings=(1 2 3)
BARthings=(a b c)

for j in ${THEBIGLOOP[*]}
do
    TheNewVariable=$(eval echo \${${j#New_}things[@]})

    for i in $TheNewVariable
        do
            echo  $j $i" hello"
        echo
    done
done

#を使用して最初の配列エントリから「New_」を削除し、次に「things」と連結して「FOOthings」を取得しています。\ $ {}にechoとevalを付けて、エラーをスローせずに順番に処理します。これは新しい$()でラップされ、新しい変数名が割り当てられます。

$ Test.sh

New_FOO 1 hello

New_FOO 2 hello

New_FOO 3 hello

New_BAR a hello

New_BAR b hello

New_BAR c hello
アップデート##### 2018/06/07

私は最近、この問題に関するもう1つのスピンを発見しました。作成される変数は実際には配列ではなく、スペースで区切られた文字列です。上記のタスクではこれは問題ありませんでした。「for」が機能するため、配列を読み取らず、展開されてからループされます。以下の抜粋を参照してください。

for VARIABLE in 1 2 3 4 5 .. N
do
    command1
    command2
    commandN
done

しかし、それを配列として使用する必要がありました。このため、もう1つの手順を実行する必要がありました。私はデニス・ウィリアムソンによるコードを逐語的に採用しました。私はそれをテストしました、そしてそれはうまく働きます。

IFS=', ' read -r -a TheNewVariable <<< ${TheNewVariable[@]}

「IFS = '、'」は、あなたの分娩誘発剤を含む変数です。"-a"を指定した "read"は、文字列を切り取り、配列変数にフィードバックします。これは引用符を考慮していませんが、これを管理するための読み取りオプションがいくつかあります。たとえば、不要な-rフラグを削除しました。そこで、この追加と変数の作成を組み合わせました。これにより、データを必要に応じて処理およびアドレス指定できます。

THEBIGLOOP=(New_FOO New_BAR)

FOOthings=(1 2 3)
BARthings=(a b c)

for j in ${THEBIGLOOP[*]}
do

    IFS=', ' read -a TheNewVariable <<< $(eval echo \${${j#New_}things[@]})

    for i in ${TheNewVariable[@]}  #Now have to wrap with {} and expand with @
        do
            echo  $j $i" hello"
            echo  ${TheNewVariable[$i]}  #This would not work in the original code
        echo
    done
done

-1

ありえない :(

配列が単純な場合は、連想配列を使用します

    declare -A array
    array[A]="1 2 3"
    array[B]="a b c"

残念ながら、配列がもっと複​​雑な場合(たとえばarray=( "a b" c ))、それは機能しません。次に、目標を達成するための別の方法についてより深く考える必要があります。


反対票の理由は何ですか?連想配列は、すべての要素にスペースが含まれていないと仮定して、すべてをグループ化する優れた方法を提供します。
アーロン、

2
@Aaron要素にスペースが含まれていないと仮定すると、これは合理的な設計です。@watael質問の主な焦点が明らかに可能である場合、答えを「無理」から始めるのは良い考えではなかったと思います。
Gilles 'SO-邪悪なことをやめなさい'

-1

使用する eval

arrayA=(1 2 3)
ARG=arrayA
eval echo \${$ARG[@]} # equivalent to eval echo \${arrayA[@]}
                      # note that we escape the first '$' to prevent from 
                      # its parameter expansion before passing it to echo

-1

これは、動的に名前が付けられた変数を作成する方法です(bashバージョン<4.3)。

# Dynamically named array
my_variable_name="dyn_arr_names"
eval $my_variable_name=\(\)

# Adding by index to the array eg. dyn_arr_names[0]="bob"
eval $my_variable_name[0]="bob"

# Adding by pushing onto the array eg. dyn_arr_names+=(robert)
eval $my_variable_name+=\(robert\)

# Print value stored at index indirect
echo ${!my_variable_name[0]}

# Print value stored at index
eval echo \${$my_variable_name[0]}

# Get item count
eval echo \${#$my_variable_name[@]}

以下は、動的に名前が付けられた配列を管理するために使用できる関数のグループです(bashバージョン<4.3)。

# Dynamically create an array by name
function arr() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
     # The following line can be replaced with 'declare -ag $1=\(\)'
     # Note: For some reason when using 'declare -ag $1' without the parentheses will make 'declare -p' fail
    eval $1=\(\)
}

# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() { 
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval $1[\$\(\(\${#${1}[@]}\)\)]=\$2
}

# Update an index by position
function arr_set() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval ${1}[${2}]=\${3}
}

# Get the array content ${array[@]}
function arr_get() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    eval echo \${${1}[@]}
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_at() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    [[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
    local v=$1
    local i=$2
    local max=$(eval echo \${\#${1}[@]})
    # Array has items and index is in range
    if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
    then 
        eval echo \${$v[$i]}
    fi
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_count() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    local v=${1}
    eval echo \${\#${1}[@]}
}



array_names=(bob jane dick)

for name in "${array_names[@]}"
do
    arr dyn_$name
done

echo "Arrays Created"
declare -a | grep "a dyn_"

# Insert three items per array
for name in "${array_names[@]}"
do
    echo "Inserting dyn_$name abc"
    arr_insert dyn_$name "abc"
    echo "Inserting dyn_$name def"
    arr_insert dyn_$name "def"
    echo "Inserting dyn_$name ghi"
    arr_insert dyn_$name "ghi"
done

for name in "${array_names[@]}"
do
    echo "Setting dyn_$name[0]=first"
    arr_set dyn_$name 0 "first"
    echo "Setting dyn_$name[2]=third"
    arr_set dyn_$name 2 "third"
done 

declare -a | grep "a dyn_"

for name in "${array_names[@]}"
do
    arr_get dyn_$name
done


for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name by index"
    # Print by index
    for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
    do
        echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"

    done
done

for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name"
    for n in $(arr_get dyn_$name)
    do
        echo $n
    done
done

以下は、動的に名前が付けられた配列(bashバージョン> = 4.3)を管理するために使用できる関数のグループです。

# Dynamically create an array by name
function arr() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -g -a $1=\(\)   
}

# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() { 
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1
    r[${#r[@]}]=$2
}

# Update an index by position
function arr_set() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1 
    r[$2]=$3
}

# Get the array content ${array[@]}
function arr_get() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1 
    echo ${r[@]}
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_at() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    [[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
    declare -n r=$1 
    local max=${#r[@]}
    # Array has items and index is in range
    if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
    then 
        echo ${r[$2]}
    fi
}

# Get the value stored at a specific index eg. ${array[0]}  
function arr_count() {
    [[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
    declare -p "$1" > /dev/null 2>&1
    [[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
    declare -n r=$1
    echo ${#r[@]}
}



array_names=(bob jane dick)

for name in "${array_names[@]}"
do
    arr dyn_$name
done

echo "Arrays Created"
declare -a | grep "a dyn_"

# Insert three items per array
for name in "${array_names[@]}"
do
    echo "Inserting dyn_$name abc"
    arr_insert dyn_$name "abc"
    echo "Inserting dyn_$name def"
    arr_insert dyn_$name "def"
    echo "Inserting dyn_$name ghi"
    arr_insert dyn_$name "ghi"
done

for name in "${array_names[@]}"
do
    echo "Setting dyn_$name[0]=first"
    arr_set dyn_$name 0 "first"
    echo "Setting dyn_$name[2]=third"
    arr_set dyn_$name 2 "third"
done 

declare -a | grep 'a dyn_'

for name in "${array_names[@]}"
do
    arr_get dyn_$name
done


for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name by index"
    # Print by index
    for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
    do
        echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"

    done
done

for name in "${array_names[@]}"
do
    echo "Dumping dyn_$name"
    for n in $(arr_get dyn_$name)
    do
        echo $n
    done
done

これらの例の詳細については、Ludvik Jerabekによる「Getting Bashed by Dynamic Arrays」を参照してください。


なぜこれが反対票を投じられるのか、私は興味があります。アプローチに問題がある/危険なものはありますか?bash <4.3で関数を使用したい。
stephenmm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.