Apa yang setara dengan kamus Python tetapi di Bash (harus bekerja di OS X dan Linux).
bash
dictionary
hashtable
associative-array
Sridhar Ratnakumar
sumber
sumber
Jawaban:
Bash 4
Bash 4 secara native mendukung fitur ini. Pastikan hashbang skrip Anda
#!/usr/bin/env bash
atau#!/bin/bash
Anda tidak menggunakannyash
. Pastikan Anda mengeksekusi skrip Anda secara langsung, atau mengeksekusiscript
denganbash script
. (Sebenarnya tidak menjalankan skrip Bash dengan Bash memang terjadi, dan akan sangat membingungkan!)Anda mendeklarasikan array asosiatif dengan melakukan:
Anda dapat mengisinya dengan elemen menggunakan operator penetapan array normal. Misalnya, jika Anda ingin memiliki peta
animal[sound(key)] = animal(value)
:Atau gabungkan mereka:
Kemudian gunakan mereka seperti array normal. Menggunakan
animals['key']='value'
untuk menetapkan nilai"${animals[@]}"
untuk memperluas nilai"${!animals[@]}"
(perhatikan!
) untuk membuka kunciJangan lupa mengutipnya:
Bash 3
Sebelum bash 4, Anda tidak memiliki array asosiatif. Jangan gunakan
eval
untuk meniru mereka . Hindarieval
seperti wabah, karena merupakan wabah shell scripting. Alasan terpenting adalah itueval
memperlakukan data Anda sebagai kode yang dapat dieksekusi (ada banyak alasan lain juga).Pertama dan terutama : Pertimbangkan untuk meningkatkan ke bash 4. Ini akan membuat seluruh proses lebih mudah bagi Anda.
Jika ada alasan Anda tidak bisa memutakhirkan, itu
declare
adalah opsi yang jauh lebih aman. Itu tidak mengevaluasi data seperti kode basheval
tidak, dan dengan demikian tidak memungkinkan injeksi kode arbitrer dengan mudah.Mari kita siapkan jawabannya dengan memperkenalkan konsep:
Pertama, tipuan.
Kedua,
declare
:Satukan mereka:
Mari kita gunakan:
Catatan:
declare
tidak bisa dimasukkan ke dalam fungsi. Setiap penggunaandeclare
fungsi bash dalam mengubah variabel yang dibuatnya lokal untuk lingkup fungsi itu, berarti kita tidak bisa akses atau memodifikasi array global dengan itu. (Dalam bash 4 Anda dapat menggunakan menyatakan -g untuk mendeklarasikan variabel global - tetapi dalam bash 4, Anda dapat menggunakan array asosiatif di tempat pertama, menghindari solusi ini.)Ringkasan:
declare -A
untuk array asosiatif.declare
opsi jika Anda tidak dapat memutakhirkan.awk
dan hindari masalah ini sama sekali.sumber
4.x
dan bukany
.sudo port install bash
, bagi mereka (secara bijak, IMHO) yang tidak ingin membuat direktori di PATH untuk semua pengguna yang dapat ditulis tanpa eskalasi hak istimewa per-proses yang eksplisit.Ada substitusi parameter, meskipun mungkin un-PC juga ... seperti tipuan.
Cara BASH 4 tentu saja lebih baik, tetapi jika Anda membutuhkan peretasan ... hanya peretasan yang akan dilakukan. Anda dapat mencari array / hash dengan teknik serupa.
sumber
VALUE=${animal#*:}
untuk melindungi kasus di manaARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
Inilah yang saya cari di sini:
Ini tidak berfungsi untuk saya dengan bash 4.1.5:
sumber
Anda selanjutnya dapat memodifikasi antarmuka hput () / hget () sehingga Anda telah menamai hash sebagai berikut:
lalu
Ini memungkinkan Anda menentukan peta lain yang tidak bertentangan (misalnya, 'rcapitals' yang melakukan pencarian negara oleh ibu kota). Tapi, bagaimanapun juga, saya pikir Anda akan menemukan bahwa ini semua cukup mengerikan, dari segi kinerja.
Jika Anda benar-benar ingin pencarian hash cepat, ada peretasan yang mengerikan, yang sebenarnya bekerja sangat baik. Ini adalah ini: tulis kunci / nilai Anda ke file sementara, satu per baris, kemudian gunakan 'grep "^ $ key"' untuk mengeluarkannya, menggunakan pipa dengan cut atau awk atau sed atau apa pun untuk mengambil nilai.
Seperti yang saya katakan, kedengarannya mengerikan, dan kedengarannya seperti itu harus lambat dan melakukan semua jenis IO yang tidak perlu, tetapi dalam praktiknya sangat cepat (cache disk mengagumkan, bukan?), Bahkan untuk hash yang sangat besar meja. Anda harus memaksakan keunikan kunci sendiri, dll. Bahkan jika Anda hanya memiliki beberapa ratus entri, file output / grep combo akan menjadi sedikit lebih cepat - dalam pengalaman saya beberapa kali lebih cepat. Ini juga memakan lebih sedikit memori.
Inilah satu cara untuk melakukannya:
sumber
Cukup gunakan sistem file
Sistem file adalah struktur pohon yang dapat digunakan sebagai peta hash. Tabel hash Anda akan menjadi direktori sementara, kunci Anda akan menjadi nama file, dan nilai Anda akan menjadi isi file. Keuntungannya adalah ia dapat menangani hashmaps besar, dan tidak memerlukan shell spesifik.
Penciptaan Hashtable
hashtable=$(mktemp -d)
Tambahkan elemen
echo $value > $hashtable/$key
Baca elemen
value=$(< $hashtable/$key)
Performa
Tentu saja, ini lambat, tetapi tidak terlalu lambat. Saya mengujinya di mesin saya, dengan SSD dan btrfs , dan itu sekitar 3000 elemen baca / tulis per detik .
sumber
mkdir -d
? (Tidak 4.3, di Ubuntu 14. Saya akan menggunakanmkdir /run/shm/foo
, atau jika itu mengisi RAMmkdir /tmp/foo
,.)mktemp -d
itu yang dimaksudkan?$value=$(< $hashtable/$key)
danvalue=$(< $hashtable/$key)
? Terima kasih!sumber
${var#start}
menghapus teks mulai dari awal nilai yang disimpan dalam variabel var .Pertimbangkan solusi menggunakan bash builtin read seperti yang diilustrasikan dalam cuplikan kode dari skrip firewall ufw yang mengikuti. Pendekatan ini memiliki keuntungan menggunakan sebanyak set bidang terbatas (tidak hanya 2) seperti yang diinginkan. Kami telah menggunakan | pembatas karena penentu rentang port mungkin memerlukan titik dua, yaitu 6001: 6010 .
sumber
IFS=$'|' read -r first rest <<< "$fields"
Saya setuju dengan @lhunath dan lainnya bahwa array asosiatif adalah cara untuk menggunakan Bash 4. Jika Anda terjebak pada Bash 3 (OSX, distro lama yang tidak dapat Anda perbarui) Anda dapat menggunakan expr, yang seharusnya ada di mana-mana, sebuah string dan ekspresi reguler. Saya suka terutama ketika kamusnya tidak terlalu besar.
Tulis peta Anda sebagai string (perhatikan pemisah ',' juga di awal dan akhir)
Gunakan regex untuk mengekstrak nilai
Pisahkan string untuk membuat daftar item
Sekarang Anda dapat menggunakannya:
sumber
Saya sangat menyukai jawaban Al P tetapi ingin keunikan ditegakkan dengan murah jadi saya mengambil satu langkah lebih jauh - menggunakan direktori. Ada beberapa batasan yang jelas (batas file direktori, nama file tidak valid) tetapi harus berfungsi untuk sebagian besar kasus.
Ini juga melakukan sedikit lebih baik dalam pengujian saya.
Kupikir aku akan ikut. Bersulang!
Edit: Menambahkan hdestroy ()
sumber
Dua hal, Anda dapat menggunakan memori alih-alih / tmp di kernel 2.6 dengan menggunakan / dev / shm (Redhat) distro lain mungkin berbeda. Hget juga dapat diimplementasikan menggunakan baca sebagai berikut:
Selain itu dengan mengasumsikan bahwa semua tombol unik, kembalikan sirkuit pendek loop baca dan mencegah harus membaca semua entri. Jika implementasi Anda dapat memiliki kunci duplikat, cukup tinggalkan kembalinya. Ini menghemat biaya membaca dan forking baik grep dan awk. Menggunakan / dev / shm untuk kedua implementasi menghasilkan berikut menggunakan waktu hget pada hash entri 3 mencari entri terakhir:
Grep / Awk:
Baca / gema:
pada banyak pemanggilan, saya tidak pernah melihat peningkatan yang kurang dari 50%. Ini semua dapat dikaitkan dengan fork over head, karena penggunaan
/dev/shm
.sumber
Seorang rekan kerja baru saja menyebutkan utas ini. Saya sudah menerapkan tabel hash secara mandiri dalam bash, dan itu tidak tergantung pada versi 4. Dari posting blog saya pada Maret 2010 (sebelum beberapa jawaban di sini ...) berjudul Tabel hash di bash :
Saya sebelumnya pernah menggunakan
cksum
hash tetapi sejak itu menerjemahkan hashCode string Java ke bash / zsh asli.Ini bukan dua arah, dan cara bawaannya jauh lebih baik, tetapi tidak pula seharusnya digunakan. Bash hanya untuk sekali saja, dan hal-hal seperti itu seharusnya jarang melibatkan kompleksitas yang mungkin membutuhkan hash, kecuali mungkin pada Anda
~/.bashrc
dan teman-teman.sumber
Sebelum bash 4 tidak ada cara yang baik untuk menggunakan array asosiatif di bash. Taruhan terbaik Anda adalah menggunakan bahasa yang ditafsirkan yang sebenarnya memiliki dukungan untuk hal-hal seperti itu, seperti awk. Di sisi lain, bash 4 melakukannya mendukung mereka.
Adapun cara-cara yang kurang baik di bash 3, berikut ini adalah referensi yang mungkin bisa membantu: http://mywiki.wooledge.org/BashFAQ/006
sumber
Solusi Bash 3:
Dalam membaca beberapa jawaban saya mengumpulkan fungsi kecil cepat saya ingin berkontribusi kembali yang dapat membantu orang lain.
sumber
Saya juga menggunakan cara bash4 tapi saya menemukan bug yang mengganggu.
Saya perlu memperbarui konten array asosiatif secara dinamis sehingga saya menggunakan cara ini:
Saya mengetahui bahwa dengan bash 4.3.11 menambahkan ke kunci yang ada di dict menghasilkan menambahkan nilai jika sudah ada. Jadi misalnya setelah beberapa kali pengulangan konten nilainya adalah "checkKOcheckKOallCheckOK" dan ini tidak baik.
Tidak ada masalah dengan bash 4.3.39 di mana appenging kunci yang ada berarti mengganti nilai aktual jika sudah ada.
Saya memecahkan ini hanya membersihkan / menyatakan array asosiatif statusCheck sebelum cicle:
sumber
Saya membuat HashMaps di bash 3 menggunakan variabel dinamis. Saya menjelaskan cara kerjanya dalam jawaban saya untuk: Array asosiatif dalam skrip Shell
Anda juga dapat melihat di shell_map , yang merupakan implementasi HashMap dibuat di bash 3.
sumber