วิธีการใช้ตัวแปรเป็นส่วนหนึ่งของชื่ออาร์เรย์


11

ฉันมีสองอาร์เรย์

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

if elseและฉันต้องการที่จะพิมพ์ออกหนึ่งของพวกเขาโดยใช้อาร์กิวเมนต์บรรทัดคำสั่งคือโดยไม่ต้องมี

ฉันลองใช้รูปแบบของไวยากรณ์ไม่สำเร็จ ฉันต้องการทำสิ่งนี้:

ARG="$1"

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

แต่ฉันได้รับข้อผิดพลาด "การแทนที่ไม่ดี" ฉันจะบรรลุสิ่งนี้ได้อย่างไร


นี่คือไม่ทุบตีอย่างเด่นชัด โปรดอย่าทำเช่นนี้
สัญลักษณ์แทน

คำตอบ:


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ตัวแปรคืออะไร? มันทำงานอย่างไรดูเหมือนว่าจะเป็นการทดแทนประวัติศาสตร์ของ googling แต่ฉันไม่เห็นวิธีการทำงานที่นี่
แอรอน

ดูโพสต์ที่แก้ไขแล้วของฉัน
Gilles Quenot

4

ในขณะที่คุณสามารถใช้การเข้าถึงทางอ้อมตามที่ระบุไว้ในคำตอบอื่นวิธีอื่น (ใน ksh และ Bash 4.3 และใหม่กว่า) จะใช้ namerefs โดยเฉพาะอย่างยิ่งในกรณีของอาร์เรย์สิ่งนี้อาจมีประโยชน์มากกว่าเนื่องจากคุณสามารถทำดัชนีอาร์เรย์ผ่าน 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

หมายเหตุ: escotes cotes ในกรณีที่มีพื้นที่ว่าง !

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

1

การทดลองใช้และข้อผิดพลาดเป็นจำนวนมาก แต่ในที่สุดก็ใช้งานได้

ฉันได้แรงบันดาลใจจาก Youness แต่คำตอบอื่น ๆ ทั้งหมดไม่ได้ช่วยทุบตีเก่าของฉัน (suse11sp1 [3.2.51 (1) - ปล่อย]]

การวนรอบ '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

ฉันเพิ่งค้นพบหมุนอีกหนึ่งเกี่ยวกับปัญหานี้ ตัวแปรที่สร้างขึ้นไม่ใช่อาร์เรย์ แต่เป็นสตริงที่คั่นด้วยช่องว่าง สำหรับงานด้านบนนี้ถือว่าใช้ได้เนื่องจากวิธี "สำหรับ" ทำงานมันไม่ได้อ่านอาเรย์นั้นจะถูกขยายและวนลูปผ่านดูที่ด้านล่างแยก:

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

แต่ฉันต้องใช้มันเป็นอาร์เรย์ สำหรับเรื่องนี้ฉันต้องดำเนินการอีกขั้นตอนหนึ่ง ผมเอาคำต่อคำรหัสโดยเดนนิสวิลเลียมสัน ฉันทดสอบแล้วและใช้งานได้ดี

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

"IFS = ','" เป็นตัวแปรที่มีตัวกำหนดของคุณ "read" with "-a" จะตัดและฟีด sting กลับเข้าไปในตัวแปรอาร์เรย์ หมายเหตุนี่ไม่เกี่ยวกับเครื่องหมายอัญประกาศ แต่มีตัวเลือกสองสามตัวที่อ่านเพื่อจัดการสิ่งนี้เช่นฉันได้ลบแฟล็ก -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

0

นี่คือวิธีที่คุณจะสร้างตัวแปรที่ตั้งชื่อแบบไดนามิก (เวอร์ชั่นทุบตี <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[@]}

ด้านล่างเป็นกลุ่มของฟังก์ชั่นที่สามารถใช้เพื่อจัดการอาร์เรย์ที่ตั้งชื่อแบบไดนามิก (เวอร์ชั่นทุบตี <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

ด้านล่างเป็นกลุ่มของฟังก์ชั่นที่สามารถใช้ในการจัดการอาร์เรย์ที่ตั้งชื่อแบบไดนามิก (เวอร์ชั่นทุบตี> = 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


1
ฉันสงสัยว่าทำไมสิ่งนี้ถึงได้รับการโหวตต่ำ มีบางสิ่งผิดปกติหรือเป็นอันตรายเมื่อเข้าใกล้ ต้องการใช้ฟังก์ชันใน bash <4.3
stephenmm

ฉันขอบคุณโพสต์ของคุณฉันพบข้อมูลที่เป็นประโยชน์และคำแนะนำ "รับจำนวนรายการ" โดยเฉพาะ: eval echo \ $ {# $ my_variable_name [@]}
Daniel Perez

-1

ไม่มีทาง :(

ถ้าอาร์เรย์ของคุณง่ายมากให้ใช้การเชื่อมโยงอาร์เรย์

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

ขออภัยถ้าอาร์เรย์ของคุณซับซ้อนกว่า (ตัวอย่างarray=( "a b" c )) นั่นจะไม่ทำงาน จากนั้นคุณต้องคิดให้หนักขึ้นเพื่อให้บรรลุเป้าหมายของคุณ


เหตุผลในการลงคะแนนคืออะไร? อาเรย์แบบเชื่อมโยงเป็นวิธีที่ดีในการจัดกลุ่มทุกอย่างโดยสมมติว่าองค์ประกอบของฉันจะไม่มีที่ว่าง
แอรอน

2
@Aaron สมมติว่าองค์ประกอบของคุณไม่มีช่องว่างนั่นคือการออกแบบที่เหมาะสม @watael ฉันเดาว่าเริ่มต้นคำตอบด้วย“ ไม่มีทาง” เมื่อจุดสนใจหลักของคำถามของคุณเป็นไปได้ชัดเจนไม่ใช่ความคิดที่ดี
Gilles 'ดังนั้นหยุดความชั่วร้าย'

-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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.