Cách sử dụng biến như một phần của tên mảng


11

Tôi có hai mảng:

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

và tôi muốn in ra một trong số chúng bằng cách sử dụng đối số dòng lệnh, tức là không có bất kỳ if else.

Tôi đã thử một vài biến thể về cú pháp nhưng không thành công. Tôi muốn làm một cái gì đó như thế này:

ARG="$1"

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

nhưng tôi nhận được một lỗi "thay thế xấu". Làm thế nào tôi có thể đạt được điều này?


Đây rõ ràng không phải là bash thành ngữ. Xin đừng làm điều này.
tự đại diện

Câu trả lời:


22

Hãy thử làm điều này:

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

GHI CHÚ

  • từ man bash(mở rộng tham số):
    ${parameter}
           The value of parameter is substituted.
 The braces are required when parameter is a positional parameter with
  more than one

chữ số hoặc khi tham số được theo sau bởi một ký tự không được hiểu là một phần của tên của nó.
* Nếu ký tự đầu tiên của tham số là dấu chấm than (!), Một mức độ biến đổi được đưa ra. Bash sử dụng giá trị của biến được hình thành từ phần còn lại của tham số làm tên của biến; biến này sau đó được mở rộng và giá trị đó được sử dụng trong phần còn lại của sự thay thế, thay vì giá trị của chính tham số. Điều này được gọi là mở rộng gián tiếp. * Các ngoại lệ cho điều này là phần mở rộng của tiền tố $ {! *} Và $ {! Name [@]} được mô tả bên dưới. Dấu chấm than phải ngay lập tức theo dấu ngoặc trái để giới thiệu cảm ứng.


Chính xác thì !làm gì trước varbiến? Làm thế nào nó hoạt động, nó dường như là sự thay thế lịch sử trên googling, nhưng tôi không thể thấy làm thế nào nó hoạt động ở đây.
Aaron

Xem bài đăng đã được chỉnh sửa của tôi
Gilles Quenot

4

Mặc dù bạn có thể sử dụng truy cập gián tiếp như được chỉ ra trong một câu trả lời khác, một cách khác (trong ksh và Bash 4.3 trở lên) sẽ là sử dụng các bảng tên. Đặc biệt trong trường hợp mảng, điều này có thể hữu ích hơn vì bạn có thể lập chỉ mục cho mảng thông qua nameref và không cần đặt chỉ mục vào biến được sử dụng làm tham chiếu.

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

Điều này không hoạt động thông qua truy cập gián tiếp:

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

Là một lập trình viên C có thể đặt nó, ${!q[1]}ở đây hoạt động như thể qlà một mảng các con trỏ, thay vì là một con trỏ tới một mảng.



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

lưu ý: escap cotes trong trường hợp không gian !

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

1

Điều này đã mất rất nhiều thử nghiệm và sai sót nhưng cuối cùng đã làm việc.

Tôi lấy một số cảm hứng từ Youness. Nhưng tất cả các câu trả lời khác không giúp ích gì cho bash cũ của tôi (suse11sp1 [3.2.51 (1) -release])

Vòng lặp 'for' đã từ chối mở rộng mảng gián tiếp, thay vào đó bạn cần mở rộng trước nó, sử dụng nó để tạo một mảng khác với tên biến mới của bạn. Ví dụ của tôi dưới đây cho thấy một vòng lặp kép, vì đó là mục đích sử dụng của tôi.

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

Tôi đang sử dụng # để xóa "New_" khỏi mục nhập mảng đầu tiên, sau đó ghép nối với "thứ", để có được "FOOthings". \ $ {} với echo và eval, sau đó thực hiện theo thứ tự mà không đưa ra các lỗi, được gói trong một $ () mới và gán tên biến mới.

$ 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
CẬP NHẬT ##### 2018/06/07

Gần đây tôi đã phát hiện thêm một spin về vấn đề này. Biến được tạo không thực sự là một mảng, mà là một chuỗi được phân tách bằng dấu cách. Đối với tác vụ trên, điều này là ổn, vì cách "cho" hoạt động, nó không đọc được mảng, nó được mở rộng và sau đó lặp lại, xem trích xuất bên dưới:

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

Nhưng, sau đó tôi cần sử dụng nó như một mảng. Đối với điều này, tôi cần phải thực hiện một bước nữa. Tôi lấy mã nguyên văn của Dennis Williamson . Tôi đã thử nó và nó hoạt động tốt.

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

"IFS = ','" là một biến chứa dấu phân cách của bạn. "Đọc" với "-a" cắt và đưa sting trở lại vào biến mảng. Lưu ý, điều này không liên quan đến dấu ngoặc kép, nhưng có một vài tùy chọn được đọc để quản lý điều này, ví dụ tôi đã xóa cờ -r mà tôi không cần. Vì vậy, bây giờ tôi đã kết hợp bổ sung này trong việc tạo biến, cho phép dữ liệu được xử lý và xử lý như bình thường.

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

Không đời nào :(

nếu mảng của bạn đơn giản, thì hãy sử dụng mảng kết hợp

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

thật không may, nếu mảng của bạn phức tạp hơn (ví dụ array=( "a b" c )), điều đó sẽ không hoạt động. Sau đó, bạn cần suy nghĩ kỹ hơn về một cách khác để đạt được mục tiêu của mình.


Lý do của downvote là gì? Mảng kết hợp cung cấp một cách tốt đẹp để nhóm tất cả mọi thứ, giả sử rằng tất cả các yếu tố của tôi sẽ không chứa không gian.
Aaron

2
@Aaron Giả sử các yếu tố của bạn không chứa khoảng trắng, đó là một thiết kế hợp lý. @Watael Tôi đoán bắt đầu câu trả lời bằng cách không có cách nào khi mà trọng tâm chính của câu hỏi của bạn rõ ràng là không thể là một ý tưởng hay.
Gilles 'SO- ngừng trở nên xấu xa'

-1

Sử dụng 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

Đây là cách bạn sẽ tạo một biến có tên động (phiên bản 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[@]}

Dưới đây là một nhóm các hàm có thể được sử dụng để quản lý các mảng được đặt tên động (phiên bản 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

Dưới đây là một nhóm các hàm có thể được sử dụng để quản lý các mảng được đặt tên động (phiên bản 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

Để biết thêm chi tiết về các ví dụ này, hãy truy cập Bắt Bashing bởi Mảng động của Ludvik Jerabek


Tôi tò mò tại sao điều này bị hạ cấp. Có điều gì sai / nguy hiểm với cách tiếp cận. Muốn sử dụng các hàm trên bash <4.3.
stephenmm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.