Cara menggunakan variabel sebagai bagian dari nama array

11

Saya punya dua array:

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

dan saya ingin mencetak salah satunya menggunakan argumen baris perintah, yaitu tanpa if else.

Saya mencoba beberapa variasi pada sintaks tetapi tidak berhasil. Saya ingin melakukan sesuatu seperti ini:

ARG="$1"

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

tapi saya mendapatkan kesalahan "substitusi buruk". Bagaimana saya bisa mencapai ini?

Harun
sumber
Ini jelas bukan bash idiomatik. Tolong, jangan lakukan ini.
Wildcard

Jawaban:

22

Coba lakukan ini:

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

CATATAN

  • dari man bash(ekspansi parameter):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

digit, atau ketika parameter diikuti oleh karakter yang tidak dapat ditafsirkan sebagai bagian dari namanya.
* Jika karakter pertama dari parameter adalah tanda seru (!), Level indirection variabel diperkenalkan. Bash menggunakan nilai variabel yang dibentuk dari sisa parameter sebagai nama variabel; variabel ini kemudian diperluas dan nilai itu digunakan di seluruh substitusi, bukan nilai parameter itu sendiri. Ini dikenal sebagai ekspansi tidak langsung. * Pengecualian untuk ini adalah ekspansi $ {! Awalan *} dan $ {! Name [@]} yang dijelaskan di bawah ini. Tanda seru harus segera mengikuti penjepit kiri untuk memperkenalkan tipuan.

Gilles Quenot
sumber
Apa sebenarnya yang !dilakukan di depan varvariabel? Bagaimana cara kerjanya, sepertinya itu pengganti sejarah pada googling, tapi saya tidak bisa melihat bagaimana itu bekerja di sini.
Aaron
Lihat posting saya yang diedit
Gilles Quenot
4

Meskipun Anda dapat menggunakan akses tidak langsung seperti yang ditunjukkan dalam jawaban lain, cara lain (dalam ksh dan Bash 4.3 dan yang lebih baru) adalah dengan menggunakan nameref. Terutama dalam hal array, ini mungkin lebih berguna karena Anda dapat mengindeks array melalui nameref dan tidak perlu menempatkan indeks dalam variabel yang digunakan sebagai referensi.

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

Ini tidak berfungsi melalui akses tidak langsung:

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

Seperti yang mungkin dikatakan oleh seorang programmer C, di ${!q[1]}sini bertindak seolah-olah qadalah array dari pointer, bukannya menjadi pointer ke array.

ilkkachu
sumber
1
Ini hanya berfungsi dalam versi bash ≥ 4.3.
G-Man Mengatakan 'Reinstate Monica'
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

Catatan: melarikan diri dari tempat tidur jika ada ruang !

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

Ini membutuhkan banyak trial and error tetapi akhirnya berhasil.

Saya mengambil beberapa inspirasi dari Youness. Tetapi semua jawaban lain tidak membantu bash lama saya (suse11sp1 [3.2.51 (1) -release])

Loop 'untuk' menolak untuk memperluas array tidak langsung, sebagai gantinya Anda perlu melakukan pra-perluasannya, menggunakannya untuk membuat array lain dengan nama variabel baru Anda. Contoh saya di bawah ini menunjukkan loop ganda, karena itulah tujuan saya.

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

Saya menggunakan # untuk menghapus "New_" dari entri array pertama, kemudian menyatukan dengan "sesuatu", untuk mendapatkan "FOOthings". \ $ {} dengan gema dan eval, kemudian lakukan tugas mereka tanpa membuang kesalahan, yang dibungkus dengan $ (baru) dan ditugaskan nama variabel baru.

$ 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
UPDATE ##### 2018/06/07

Saya baru-baru ini menemukan satu putaran lagi tentang masalah ini. Variabel yang dibuat sebenarnya bukan array, tetapi string yang dibatasi ruang. Untuk tugas di atas ini ok, karena bagaimana "untuk" bekerja, tidak membaca array, diperluas dan kemudian diulang, lihat ekstrak di bawah ini:

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

Tapi, saya kemudian perlu menggunakannya sebagai array. Untuk ini saya perlu melakukan satu langkah lagi. Saya mengambil kode kata demi kata oleh Dennis Williamson . Saya sudah mengujinya dan berfungsi dengan baik.

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

"IFS = ','" adalah variabel yang mengandung pembatas Anda. "baca" dengan "-a" memotong dan memasukkan sengatan kembali ke variabel array. Catatan, ini tidak menghormati tanda kutip, tetapi ada beberapa opsi dalam membaca untuk mengelola ini, misalnya saya telah menghapus tanda -r yang tidak saya butuhkan. Jadi saya sekarang telah menggabungkan penambahan ini dalam pembuatan variabel, yang memungkinkan data untuk diperlakukan dan ditangani sebagaimana mestinya.

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
Stripy42
sumber
-1

tidak mungkin :(

jika array Anda sesederhana itu, gunakan array asosiatif

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

Sayangnya, jika array Anda lebih rumit (misalnya array=( "a b" c )), itu tidak akan berfungsi. Kemudian, Anda perlu berpikir lebih keras tentang cara lain untuk mencapai tujuan Anda.

watael
sumber
Apa alasan downvote? Array asosiatif menyediakan cara yang bagus untuk mengelompokkan segala sesuatu, dengan asumsi bahwa elemen saya semua tidak mengandung ruang.
Aaron
2
@ Harun Dengan asumsi elemen Anda tidak mengandung spasi, itu adalah desain yang masuk akal. @Watael Saya kira memulai jawaban dengan "tidak mungkin" ketika fokus utama pertanyaan Anda jelas mungkin bukan ide yang baik.
Gilles 'SO- stop being evil'
-1

Menggunakan 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
MS.Kim
sumber
-1

Ini adalah bagaimana Anda akan membuat variabel bernama dinamis (versi 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[@]}

Di bawah ini adalah sekelompok fungsi yang dapat digunakan untuk mengelola array bernama dinamis (versi 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

Di bawah ini adalah sekelompok fungsi yang dapat digunakan untuk mengelola array bernama dinamis (versi 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

Untuk detail lebih lanjut tentang contoh-contoh ini kunjungi Getting Bashed oleh Dynamic Arays oleh Ludvik Jerabek

NOPx90
sumber
Saya ingin tahu mengapa ini diturunkan. Apakah ada yang salah / berbahaya dengan pendekatan tersebut. Ingin menggunakan fungsi di bash <4.3.
stephenmm