Saya pikir Anda menanyakan dua hal berbeda di sana.
Apakah ada cara untuk membuat bash mencetak info ini tanpa loop?
Ya, tetapi mereka tidak sebagus hanya menggunakan loop.
Apakah ada cara yang lebih bersih untuk mendapatkan / mencetak hanya bagian kunci = nilai dari output?
Ya itu for
simpulnya. Ini memiliki kelebihan yang tidak memerlukan program eksternal, mudah, dan membuatnya lebih mudah untuk mengontrol format output yang tepat tanpa kejutan.
Solusi apa pun yang mencoba menangani output dari declare -p
(typeset -p
) harus berurusan dengan a) kemungkinan variabel itu sendiri mengandung tanda kurung atau tanda kurung, b) kutipan yang declare -p
harus ditambahkan untuk membuat output itu valid input untuk shell.
Misalnya, ekspansi Anda b="${a##*(}"
memakan beberapa nilai, jika ada kunci / nilai yang berisi tanda kurung buka. Ini karena Anda menggunakan ##
, yang menghilangkan awalan terpanjang . Sama untukc="${b%% )*}"
. Meskipun tentu saja Anda dapat mencocokkan pelat tungku yang dicetak dengan declare
lebih tepat, Anda masih akan mengalami kesulitan jika Anda tidak ingin semua kutipannya cocok.
Ini tidak terlihat bagus kecuali Anda membutuhkannya.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
Dengan for
loop, lebih mudah untuk memilih format output sesuka Anda:
# 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\'
Dari sana, juga mudah untuk mengubah format output jika tidak (hapus tanda kurung di sekitar kunci, letakkan semua pasangan kunci / nilai pada satu baris ...). Jika Anda perlu mengutip sesuatu selain dari shell itu sendiri, Anda masih perlu melakukannya sendiri, tetapi setidaknya Anda memiliki data mentah untuk dikerjakan. (Jika Anda memiliki baris baru di kunci atau nilai, Anda mungkin perlu mengutip.)
Dengan Bash saat ini (4,4, saya pikir), Anda juga bisa menggunakan printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
bukan printf "%q=%q"
. Ini menghasilkan format kutipan yang agak lebih bagus, tetapi tentu saja sedikit lebih banyak pekerjaan yang perlu diingat untuk menulis. (Dan itu mengutip kasus sudut @
sebagai kunci array, yang %q
tidak mengutip.)
Jika for for tampaknya terlalu lelah untuk ditulis, simpan fungsi di suatu tempat (tanpa mengutip di sini):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
Dan kemudian gunakan itu:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Bekerja dengan array yang diindeks juga:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
printf ...%q...
varian Anda tidak cocok untuk reinput ke shell jika array memiliki@
kunci karena% q tidak mengutipnya dana=([@]=value)
merupakan kesalahan sintaksis dalambash
."${x@Q}"
mengutip itu juga, karena mengutip semua string (dan terlihat lebih bagus). menambahkan catatan tentang menggunakan itu.zsh
dengan flag ekspansi variabelnya (yang lagi mendahului bash oleh dekade dan dengan mana Anda dapat memilih gaya kutipan: $ {(q) var}, $ {(qq) var} ...) untuk desain yang lebih baik. bash memiliki masalah yang sama dengan mksh karena tidak mengutip string kosong (bukan masalah di sini karena bagaimanapun bash tidak mendukung kunci kosong). Juga, ketika menggunakan mengutip gaya selain kutip tunggal (${var@Q}
resort untuk$'...'
untuk beberapa nilai) penting bahwa kode menjadi reinput di lokasi yang sama.x=; echo "${x@Q}"
Apakah memberi''
,unset x; echo "${x@Q}"
tidak memberi apa-apa.) Bash@Q
tampaknya lebih suka$'\n'
daripada baris baru yang sebenarnya, yang mungkin sebenarnya baik dalam beberapa situasi (tapi saya tidak bisa mengatakan apa yang disukai orang lain). Tentu saja memiliki pilihan tidak akan ada hal buruk.$'...'
sintaks adalah masalah potensial dalam hal-hal sepertiLC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'
yang output$'\n<0xa3><0x5c>'
dan0x5c
sendirian backslash sehingga Anda akan memiliki masalah jika kutipan yang ditafsirkan di lokasi yang berbeda.2 garpu
Mungkin ini:
3 garpu
atau ini:
Tanpa garpu
untuk dibandingkan dengan
Perbandingan waktu eksekusi
Karena sintaks terakhir tidak menggunakan fork, mereka bisa lebih cepat:
Tetapi penegasan ini tidak tetap benar jika array menjadi besar; jika mengurangi garpu efisien untuk proses kecil, menggunakan alat khusus lebih efisien untuk proses yang lebih besar.
Ucapan
Karena kedua ( bercabang ) solusi menggunakan perataan , tidak satupun dari mereka akan bekerja jika ada variabel yang berisi baris baru . Dalam hal ini, satu-satunya cara adalah
for
loop.sumber
for
. Sayang sekali, sungguh.pr
lebih pendek ... Saya tidak yakin tentangpr
sintaks tetap lebih lambat, bahkan dengan array besar!${!array[@]}
dan${array[@]}
pertama untuk itu berfungsi.paste
adalah lebih lama daripadafor
loop dalam pertanyaan tertulis pada satu barisfor i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done
, tetapi membutuhkan dua subshells dan program eksternal. Bagaimana itu lebih rapi? Solusi denganpr
juga rusak jika ada banyak elemen, karena mencoba untuk memberi peringkat pada output. Anda harus menggunakan sesuatu seperti| pr -2t -l"${#array[@]}"
yang mulai sulit diingat dibandingkan dengan loop sederhana, dan sekali lagi, lebih lama dari itu.bash
,cmd1 | cmd2
berarti 2 garpu, bahkan jika cmd1 atau cmd2 atau keduanya dibangun.Jika Anda mencari shell dengan dukungan array asosiatif yang lebih baik, coba
zsh
.Dalam
zsh
(di mana array asosiatif ditambahkan pada tahun 1998, dibandingkan dengan 1993 untuk ksh93 dan 2009 untuk bash),$var
atau${(v)var}
mengembang ke (non-kosong) nilai hash,${(k)var}
untuk (non-kosong) kunci (dalam urutan yang sama), dan${(kv)var}
untuk kedua kunci dan nilai.Untuk mempertahankan nilai kosong, seperti untuk array, Anda perlu mengutip dan menggunakan
@
bendera.Jadi untuk mencetak kunci dan nilai, itu hanya masalah
Meskipun untuk memperhitungkan hash yang mungkin kosong, Anda harus melakukan:
Perhatikan juga bahwa zsh menggunakan sintaks definisi array yang jauh lebih masuk akal dan berguna daripada
ksh93
(disalin olehbash
):Yang membuatnya lebih mudah untuk menyalin atau menggabungkan array asosiatif:
(Anda tidak dapat dengan mudah menyalin hash tanpa loop dengan
bash
, dan perhatikan bahwabash
saat ini tidak mendukung kunci kosong atau kunci / nilai dengan NUL byte).Lihat juga
zsh
berbagai fitur zipping yang biasanya perlu Anda gunakan dengan array asosiatif:sumber
Karena typeset melakukan apa yang Anda inginkan mengapa tidak mengedit outputnya saja?
memberi
Dimana
Verbose tetapi cukup mudah untuk melihat bagaimana pemformatan bekerja: cukup jalankan pipeline dengan semakin banyak perintah sed dan tr . Ubah mereka agar sesuai dengan selera cetak yang cantik.
sumber
sed
dantr
bahkan tidak lebih sederhana dari satufor
lingkaran denganprintf
.tr
menerjemahkan karakter per karakter, tidak cocok dengan string?tr "]=" " ="
perubahan "]" ke spasi dan=
ke=
, terlepas dari posisi. Jadi Anda mungkin bisa menggabungkan ketiganyatr
menjadi satu.Satu lagi opsi adalah untuk mendaftar semua variabel dan grep untuk yang Anda inginkan.
set | grep -e '^aa='
Saya menggunakan ini untuk debugging. Saya ragu itu sangat berkinerja karena daftar semua variabel.
Jika Anda sering melakukan ini, Anda bisa membuatnya berfungsi seperti ini:
aap() { set | grep -e "^$1="; }
Sayangnya ketika kami memeriksa kinerja menggunakan waktu:
$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s
Karena itu, jika Anda melakukan ini sangat sering, Anda ingin versi NO FORKS dari @ F.Hauri karena jauh lebih cepat.
sumber