Kami memerlukan skrip yang mensimulasikan array Asosiatif atau Peta seperti struktur data untuk Skrip Shell, badan apa saja?
bash
shell
hashtable
associative-array
Irfan Zulfiqar
sumber
sumber
Pilihan lain, jika portabilitas bukan perhatian utama Anda, adalah menggunakan array asosiatif yang dibangun di dalam shell. Ini seharusnya berfungsi di bash 4.0 (sekarang tersedia di sebagian besar distro utama, meskipun tidak di OS X kecuali Anda menginstalnya sendiri), ksh, dan zsh:
Tergantung pada shell, Anda mungkin perlu melakukan
typeset -A newmap
bukandeclare -A newmap
, atau dalam beberapa hal mungkin tidak diperlukan sama sekali.sumber
test -z ${variable+x}
(x
tidak masalah, itu bisa berupa string apa pun). Untuk array asosiatif di Bash, Anda bisa melakukan hal serupa; gunakantest -z ${map[key]+x}
.Cara 4 non-bash lainnya.
Anda juga bisa melempar pernyataan if untuk mencari di sana. jika [[$ var = ~ / blah /]]. atau terserah.
sumber
Saya pikir Anda perlu melangkah mundur dan memikirkan tentang apa itu peta, atau array asosiatif, sebenarnya. Semua itu adalah cara untuk menyimpan nilai untuk kunci tertentu, dan mendapatkan kembali nilai itu dengan cepat dan efisien. Anda mungkin juga ingin dapat mengulang kunci untuk mengambil setiap pasangan nilai kunci, atau menghapus kunci dan nilai yang terkait.
Sekarang, pikirkan tentang struktur data yang Anda gunakan sepanjang waktu dalam skrip shell, dan bahkan hanya di shell tanpa menulis skrip, yang memiliki properti ini. Bingung? Itu sistem file.
Sungguh, yang Anda butuhkan untuk memiliki array asosiatif dalam pemrograman shell adalah direktori temp.
mktemp -d
adalah konstruktor array asosiatif Anda:Jika Anda tidak ingin menggunakan
echo
dancat
, Anda selalu dapat menulis bungkus kecil; yang ini dimodelkan dari Irfan, meskipun mereka hanya menampilkan nilai daripada menyetel variabel arbitrer seperti$value
:sunting : Pendekatan ini sebenarnya sedikit lebih cepat daripada pencarian linier menggunakan sed yang disarankan oleh penanya, serta lebih kuat (memungkinkan kunci dan nilai untuk memuat -, =, spasi, qnd ": SP:"). Fakta bahwa ia menggunakan sistem berkas tidak membuatnya lambat; file-file ini sebenarnya tidak pernah dijamin akan ditulis ke disk kecuali Anda memanggil
sync
; untuk file sementara seperti ini dengan masa hidup yang singkat, bukan tidak mungkin banyak dari mereka tidak akan pernah ditulis ke disk.Saya melakukan beberapa tolok ukur kode Irfan, modifikasi kode Irfan oleh Jerry, dan kode saya, menggunakan program driver berikut:
Hasil:
sumber
Bash4 mendukung ini secara native. Jangan gunakan
grep
ataueval
, mereka adalah peretasan paling jelek.Untuk jawaban verbose dan mendetail dengan kode contoh lihat: /programming/3467959
sumber
Contoh:
sumber
Sekarang menjawab pertanyaan ini.
Skrip berikut mensimulasikan array asosiatif dalam skrip shell. Sederhana dan sangat mudah dimengerti.
Peta tidak lain adalah string yang tidak pernah berakhir yang memiliki keyValuePair yang disimpan sebagai --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company
spasi diganti dengan ': SP:' untuk nilai
edit: Baru saja menambahkan metode lain untuk mengambil semua kunci.
sumber
eval
mengumpulkan data seolah-olah itu adalah kode bash, dan terlebih lagi: Anda gagal mengutipnya dengan benar. Keduanya menyebabkan banyak bug dan injeksi kode arbitrer.Untuk Bash 3, ada kasus tertentu yang memiliki solusi yang bagus dan sederhana:
Jika Anda tidak ingin menangani banyak variabel, atau kunci hanyalah pengenal variabel yang tidak valid, dan array Anda dijamin memiliki kurang dari 256 item , Anda dapat menyalahgunakan nilai pengembalian fungsi. Solusi ini tidak memerlukan subkulit karena nilainya sudah tersedia sebagai variabel, atau iterasi apa pun sehingga performa menjerit. Juga sangat mudah dibaca, hampir seperti versi Bash 4.
Ini versi paling dasar:
Ingat, gunakan tanda kutip tunggal dalam
case
, jika tidak, itu tunduk pada globbing. Sangat berguna untuk hash statis / beku sejak awal, tetapi seseorang dapat menulis generator indeks darihash_keys=()
array.Hati-hati, defaultnya adalah yang pertama, jadi Anda mungkin ingin menyisihkan elemen ke nol:
Peringatan: panjangnya sekarang salah.
Atau, jika Anda ingin mempertahankan pengindeksan berbasis nol, Anda dapat memesan nilai indeks lain dan menjaga dari kunci yang tidak ada, tetapi kurang terbaca:
Atau, untuk menjaga panjangnya tetap benar, offset indeks satu per satu:
sumber
Anda dapat menggunakan nama variabel dinamis dan membiarkan nama variabel berfungsi seperti kunci dari hashmap.
Misalnya, jika Anda memiliki file input dengan dua kolom, nama, kredit, seperti contoh di bawah ini, dan Anda ingin menjumlahkan pendapatan setiap pengguna:
Perintah di bawah ini akan menjumlahkan semuanya, menggunakan variabel dinamis sebagai kunci, dalam bentuk peta _ $ {person} :
Untuk membaca hasilnya:
Outputnya adalah:
Menguraikan teknik ini, saya mengembangkan di GitHub sebuah fungsi yang bekerja seperti Objek HashMap , shell_map .
Untuk membuat " instance HashMap ", fungsi shell_map dapat membuat salinannya sendiri dengan nama yang berbeda. Setiap salinan fungsi baru akan memiliki variabel $ FUNCNAME yang berbeda. $ FUNCNAME kemudian digunakan untuk membuat namespace untuk setiap instance Peta.
Kunci peta adalah variabel global, dalam bentuk $ FUNCNAME_DATA_ $ KEY, di mana $ KEY adalah kunci yang ditambahkan ke Peta. Variabel ini adalah variabel dinamis .
Di bawah ini saya akan memberikan versi yang disederhanakan sehingga Anda dapat menggunakan sebagai contoh.
Pemakaian:
sumber
Namun cara non-bash-4 lainnya (yaitu, bash 3, kompatibel dengan Mac):
Cetakan:
Fungsi dengan
case
tindakan seperti array asosiatif. Sayangnya itu tidak bisa digunakanreturn
, jadi harusecho
outputnya, tapi ini bukan masalah, kecuali Anda seorang purist yang menghindari forking subkulit.sumber
Sayang sekali saya tidak melihat pertanyaan sebelumnya - saya telah menulis library shell-framework yang berisi antara lain maps (Associative arrays). Versi terakhirnya dapat ditemukan di sini .
Contoh:
sumber
Menambahkan opsi lain, jika jq tersedia:
sumber
Saya merasa benar, seperti yang telah disebutkan, bahwa metode berkinerja terbaik adalah menulis kunci / vals ke file, dan kemudian menggunakan grep / awk untuk mengambilnya. Kedengarannya seperti semua jenis IO yang tidak perlu, tetapi cache disk masuk dan membuatnya sangat efisien - jauh lebih cepat daripada mencoba menyimpannya dalam memori menggunakan salah satu metode di atas (seperti yang ditunjukkan oleh benchmark).
Inilah metode cepat dan bersih yang saya suka:
Jika Anda ingin menerapkan nilai tunggal per kunci, Anda juga dapat melakukan sedikit tindakan grep / sed di hput ().
sumber
Beberapa tahun yang lalu saya menulis pustaka skrip untuk bash yang mendukung array asosiatif di antara fitur-fitur lain (logging, file konfigurasi, dukungan tambahan untuk argumen baris perintah, menghasilkan bantuan, pengujian unit, dll). Pustaka berisi pembungkus untuk array asosiatif dan secara otomatis beralih ke model yang sesuai (internal untuk bash4 dan meniru untuk versi sebelumnya). Itu disebut kerangka-shell dan dihosting di origo.ethz.ch tetapi hari ini sumber daya ditutup. Jika seseorang masih membutuhkannya, saya dapat membagikannya dengan Anda.
sumber
Shell tidak memiliki peta bawaan seperti struktur data, saya menggunakan string mentah untuk mendeskripsikan item seperti itu:
ketika mengekstrak item dan atributnya:
Ini sepertinya tidak pandai dari jawaban orang lain, tetapi mudah dimengerti untuk orang baru untuk dikupas.
sumber
Saya memodifikasi solusi Vadim dengan yang berikut:
Perubahannya adalah map_get untuk mencegahnya mengembalikan kesalahan jika Anda meminta kunci yang tidak ada, meskipun efek sampingnya adalah ia juga akan secara diam-diam mengabaikan peta yang hilang, tetapi lebih cocok untuk kasus penggunaan saya karena saya baru saja ingin memeriksa kunci untuk melewatkan item dalam satu lingkaran.
sumber
Balas terlambat, tetapi pertimbangkan untuk mengatasi masalah dengan cara ini, menggunakan bash builtin yang dibaca seperti yang diilustrasikan dalam cuplikan kode dari skrip firewall ufw yang mengikuti. Pendekatan ini memiliki keuntungan menggunakan sebanyak mungkin kumpulan bidang yang dibatasi (tidak hanya 2) seperti yang diinginkan. Kami telah menggunakan | pembatas karena penentu kisaran port mungkin memerlukan titik dua, yaitu 6001: 6010 .
sumber