Tôi nghĩ bạn đang hỏi hai điều khác nhau ở đó.
Có cách nào để làm bash in thông tin này mà không cần vòng lặp không?
Có, nhưng chúng không tốt như chỉ sử dụng vòng lặp.
Có cách nào sạch hơn để chỉ nhận / in phần key = value của đầu ra không?
Vâng, for
vòng lặp. Nó có những ưu điểm mà nó không yêu cầu các chương trình bên ngoài, đơn giản và giúp dễ dàng kiểm soát định dạng đầu ra chính xác mà không gây bất ngờ.
Bất kỳ giải pháp nào cố gắng xử lý đầu ra của declare -p
( typeset -p
) đều phải xử lý a) khả năng chính các biến chứa dấu ngoặc đơn hoặc dấu ngoặc, b) trích dẫn declare -p
phải thêm vào để làm đầu ra hợp lệ cho đầu ra của nó.
Ví dụ: bản mở rộng của bạn b="${a##*(}"
ăn một số giá trị, nếu bất kỳ khóa / giá trị nào chứa dấu ngoặc đơn mở. Điều này là do bạn đã sử dụng ##
, loại bỏ tiền tố dài nhất . Tương tự cho c="${b%% )*}"
. Mặc dù tất nhiên bạn có thể khớp với bản soạn sẵn được in declare
chính xác hơn, bạn vẫn sẽ gặp khó khăn nếu bạn không muốn tất cả các trích dẫn.
Điều này trông không đẹp lắm trừ khi bạn cần nó.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
Với for
vòng lặp, việc chọn định dạng đầu ra như bạn muốn sẽ dễ dàng hơn:
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
Từ đó, thật đơn giản để thay đổi định dạng đầu ra (loại bỏ các dấu ngoặc quanh khóa, đặt tất cả các cặp khóa / giá trị trên một dòng ...). Nếu bạn cần trích dẫn cho một cái gì đó không phải là chính nó, bạn vẫn sẽ cần phải tự làm điều đó, nhưng ít nhất bạn có dữ liệu thô để làm việc. (Nếu bạn có dòng mới trong các khóa hoặc giá trị, có lẽ bạn sẽ cần một số trích dẫn.)
Với một Bash hiện tại (4.4, tôi nghĩ), bạn cũng có thể sử dụng printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
thay vì printf "%q=%q"
. Nó tạo ra một định dạng trích dẫn có phần đẹp hơn, nhưng tất nhiên là phải nhớ thêm một chút công việc để viết. (Và nó trích dẫn trường hợp góc @
là khóa mảng, %q
không trích dẫn.)
Nếu vòng lặp for có vẻ quá mệt mỏi để viết, hãy lưu nó vào một hàm ở đâu đó (không trích dẫn ở đây):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
Và sau đó chỉ cần sử dụng:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Cũng hoạt động với các mảng được lập chỉ mục:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
printf ...%q...
biến thể của bạn không phù hợp để cung cấp lại cho shell nếu mảng có@
khóa là% q không trích dẫn nó vàa=([@]=value)
là một lỗi cú phápbash
.