Trả về gián tiếp tất cả các phần tử trong một mảng


9

Trang man Bash mô tả việc sử dụng ${!a}để trả về nội dung của biến có tên là nội dung của a(một mức độ không xác định).

Tôi muốn biết làm thế nào để trả về tất cả các phần tử trong một mảng bằng cách sử dụng cái này, nghĩa là

a=(one two three)
echo ${a[*]}

trả lại

one two three

Tôi muốn cho:

b=a
echo ${!b[*]}

để trả lại như cũ. Thật không may, nó không, nhưng 0thay vào đó trở lại .

Cập nhật

Đưa ra các câu trả lời, bây giờ tôi nhận ra rằng ví dụ của tôi quá đơn giản, vì dĩ nhiên, một cái gì đó như:

b=("${a[@]}")

Sẽ đạt được chính xác những gì tôi nói tôi cần.

Vì vậy, đây là những gì tôi đã cố gắng làm:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

Tất nhiên, đọc kỹ trang man Bash cho thấy điều này sẽ không hoạt động như mong đợi vì dòng cuối cùng chỉ trả về các chỉ số của "mảng" $_LIST(hoàn toàn không phải là một mảng).

Trong mọi trường hợp, sau đây nên thực hiện công việc (như đã chỉ ra):

LIST=($(eval echo \${$_LIST[*]}))

hoặc ... (tuyến đường mà tôi đã đi, cuối cùng):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

Tất nhiên, giả sử rằng các phần tử không chứa khoảng trắng.


Thêm [@]vào con trỏ _LIST="LIST_${whichone}[@]"và sau đó, sử dụng LIST=("${!_LIST}")để sao chép mảng. Đó là một ý tưởng tốt để sử dụng tên biến chữ thường để tránh xung đột với biến môi trường (Tất cả chữ hoa).
Isaac

Câu trả lời:


6

Tôi nghĩ rằng việc sử dụng tham chiếu gián tiếp của biến bash nên được xử lý theo nghĩa đen.

Ví dụ. Ví dụ ban đầu của bạn:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

Đối với kịch bản thực tế cuối cùng, tôi tin rằng đoạn mã dưới đây sẽ thực hiện công việc.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

Tốt hơn là sử dụng ký hiệu "${var[@]}"để tránh gây rối với việc $IFSmở rộng và tham số. Đây là mã cuối cùng.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted

8

Bạn cần sao chép các yếu tố một cách rõ ràng. Đối với một mảng được lập chỉ mục:

b=("${a[@]}")

Đối với một mảng kết hợp (lưu ý đó alà tên của biến mảng, không phải là biến có giá trị là tên của biến mảng):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

Nếu bạn có tên biến trong một mảng, bạn có thể sử dụng phương thức từng phần tử với một bước bổ sung để truy xuất các khóa.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Cảnh báo, mã trong bài đăng này đã được nhập trực tiếp vào trình duyệt và không được kiểm tra.)


Bạn hoàn toàn đúng, nhưng thật không may, điều đó không giải quyết được vấn đề của tôi (ví dụ của tôi quá đơn giản). Tôi đã cung cấp một bản cập nhật để rõ ràng hơn về ý định của tôi.
Eric Smith

@Eric Tôi nghĩ trong ksh / bash bạn cần eval ở giai đoạn đó. Xem chỉnh sửa của tôi.
Gilles 'SO- ngừng trở nên xấu xa'

+1, nhưng theo từ chối trách nhiệm của bạn, điều này thực sự không hoạt động (bit mã cuối cùng), nhưng chỉ cần một vài điều chỉnh nhỏ. Cụ thể, nó phải nằm \${!$name[@]}trên dòng đầu tiên, để bản mở rộng đầu tiên chỉ là '$ name', và ${!a[@]}được lưu cho eval, và điều tương tự trong vòng lặp for, với \${$name}. Hai dấu gạch chéo ngược khác trước '$ k' cũng không thực sự cần thiết ở đó.
krb686

2

${!b[*]}mở rộng đến các chỉ số được sử dụng trong mảng b.

Những gì bạn muốn phải được thực hiện trong hai bước, vì vậy evalsẽ giúp : eval echo \${$b[*]}. (Lưu ý \rằng đảm bảo rằng bước đầu tiên $sẽ vượt qua bước đầu tiên, mở rộng biến và sẽ chỉ được mở rộng trong bước thứ hai eval.)

Theo Parameter Expansion !đều được sử dụng để mở rộng gián tiếp ( {!a}), Tên khớp với tiền tố ( ${!a*}) và Danh sách các khóa mảng ( ${!a[*]}). Vì Danh sách các khóa mảng có cùng cú pháp như mở rộng gián tiếp dự định + mở rộng phần tử mảng, nên sau này không được hỗ trợ.


2
${!a}không mở rộng đến giá trị của biến có tên $a. Điều này được mô tả khá chặt chẽ trong hướng dẫn sử dụng, trong biểu đồ bắt đầu bằng chữ Nếu ký tự đầu tiên của tham số là dấu chấm than ( !), một mức độ của biến đổi được đưa ra.
Gilles 'SO- ngừng trở nên xấu xa'

Đúng - @Gilles đúng, nhưng @manatwork, trong lần đọc thứ hai, tôi nhận thấy điều đó ${!thật tuyệt vời vì nếu đó là một mảng bạn đang xử lý, thì hành vi sẽ khác.
Eric Smith

@Gilles bạn đúng với câu đó, nhưng thật đáng buồn, nó không áp dụng như "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." Nhưng câu trả lời của tôi chắc chắn là một mớ hỗn độn mơ hồ, vì vậy tôi sẽ chỉnh sửa nó.
manatwork

0

Để truy cập mảng một cách gián tiếp, chỉ cần thêm [@]vào biến gián tiếp b=a[@].

Nếu biến này được đặt:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Sau đó, điều này sẽ hoạt động:

b="a[@]"
printf '<%s> ' "${!b}"

Hoặc đơn giản:

echo "${!b}"

Mảng như vậy có thể được sao chép như sau:

newArr=( "${!b}" )

Và sau đó được in bằng:

declare -p newArr

Trong một kịch bản:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Tất nhiên, tất cả những điều trên sẽ sao chép một mảng không thưa thớt. Một trong đó tất cả các chỉ mục có một giá trị.

mảng thưa thớt

Một mảng thưa thớt là một mảng có thể có các phần tử không xác định.
Ví dụ: a[8]=1234định nghĩa một phần tử và trong bash, 0 đến 7 không tồn tại.

Để sao chép mảng thưa thớt như vậy, sử dụng phương pháp này

  1. In mảng cũ:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. Thay thế tên của mảng và bắt chuỗi:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Đánh giá chuỗi đã tạo, mảng mới đã được xác định:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
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.