Saya perlu menghapus elemen dari array di bash shell. Umumnya saya hanya melakukan:
array=("${(@)array:#<element to remove>}")
Sayangnya elemen yang ingin saya hapus adalah variabel jadi saya tidak dapat menggunakan perintah sebelumnya. Berikut contohnya:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Ada ide?
zsh
.array=( ${array[@]/$delete} )
bekerja seperti yang diharapkan di Bash. Apakah Anda melewatkan=
?Jawaban:
Berikut ini adalah pekerjaan yang Anda inginkan
bash
danzsh
:Jika perlu menghapus lebih dari satu elemen:
Peringatan
Teknik ini sebenarnya menghilangkan pencocokan prefiks
$delete
dari elemen, tidak harus seluruh elemen.Memperbarui
Untuk benar-benar menghapus item yang tepat, Anda perlu menelusuri larik, membandingkan target dengan setiap elemen, dan menggunakan
unset
untuk menghapus pencocokan tepat.Perhatikan bahwa jika Anda melakukan ini, dan satu atau lebih elemen dihapus, indeks tidak lagi menjadi urutan bilangan bulat yang berkelanjutan.
Fakta sederhananya adalah, array tidak dirancang untuk digunakan sebagai struktur data yang bisa berubah. Mereka terutama digunakan untuk menyimpan daftar item dalam satu variabel tanpa perlu membuang karakter sebagai pembatas (misalnya, untuk menyimpan daftar string yang dapat berisi spasi).
Jika celah menjadi masalah, maka Anda perlu membangun kembali larik untuk mengisi celah tersebut:
sumber
$ array=(sun sunflower)
$ delete=(sun)
$ echo ${array[@]/$delete}
menghasilkanflower
(pluto1 pluto2 pippo)
maka Anda akan berakhir dengan(1 2 pippo)
.for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Anda bisa membuat array baru tanpa elemen yang tidak diinginkan, lalu menetapkannya kembali ke array lama. Ini bekerja di
bash
:Ini menghasilkan:
sumber
Ini adalah cara paling langsung untuk menghapus nilai jika Anda mengetahui posisinya.
sumber
echo ${array[1]}
, Anda akan mendapatkan string nol. Dan untuk mendapatkanthree
yang perlu Anda lakukanecho ${array[2]}
. Jadiunset
bukan mekanisme yang tepat untuk menghapus elemen dalam bash array.${array[1]+x}
adalah string nol, jadiarray[1]
tidak disetel.unset
tidak mengubah indeks elemen yang tersisa. Mengutip argumen untuk tidak disetel tidak diperlukan. Cara untuk menghancurkan elemen array dijelaskan dalam manual Bash .${array[1]}
ada hanya karena ukurannya 2. Jika Anda menginginkan indeks, centang${!array[@]}
.Berikut solusi satu baris dengan mapfile:
Contoh:
Metode ini memungkinkan fleksibilitas tinggi dengan memodifikasi / menukar perintah grep dan tidak meninggalkan string kosong di dalam array.
sumber
printf '%s\n' "${array[@]}"
alih-alih bendaIFS
/ jelekecho
itu.-d $'\0'
bekerja dengan baik sementara hanya-d
tanpa argumen tidak.-d $'\0'
sama dengan-d $'\0 something'
atau adil-d ''
.$'\0'
untuk kejelasanJawaban ini khusus untuk kasus penghapusan beberapa nilai dari array besar, di mana kinerja itu penting.
Solusi yang paling banyak dipilih adalah (1) substitusi pola pada array, atau (2) iterasi atas elemen array. Yang pertama cepat, tetapi hanya dapat menangani elemen yang memiliki awalan berbeda, yang kedua memiliki O (n * k), n = ukuran larik, k = elemen yang akan dihapus. Array asosiatif adalah fitur relatif baru, dan mungkin tidak umum saat pertanyaan pertama kali diposting.
Untuk kasus pencocokan tepat, dengan n dan k besar, mungkin untuk meningkatkan kinerja dari O (n k) ke O (n + k log (k)). Dalam prakteknya, O (n) mengasumsikan k jauh lebih rendah dari n. Sebagian besar percepatan didasarkan pada penggunaan array asosiatif untuk mengidentifikasi item yang akan dihapus.
Performa (ukuran n-array, nilai-k yang akan dihapus). Performa mengukur detik waktu pengguna
Seperti yang diharapkan,
current
penyelesaiannya linier terhadap N * K, danfast
solusinya secara praktis linier ke K, dengan konstanta yang jauh lebih rendah. Thefast
solusi adalah sedikit lebih lambat vscurrent
solusi ketika k = 1, karena pengaturan tambahan.Solusi 'Cepat': array = daftar input, hapus = daftar nilai yang akan dihapus.
Dibandingkan dengan
current
solusi, dari jawaban yang paling banyak dipilih.sumber
Berikut adalah fungsi kecil (mungkin sangat spesifik-bash) yang melibatkan tipuan variabel bash dan
unset
; ini adalah solusi umum yang tidak melibatkan substitusi teks atau membuang elemen kosong dan tidak memiliki masalah dengan kutipan / spasi dll.Gunakan seperti
delete_ary_elmt ELEMENT ARRAYNAME
tanpa$
sigil. Ganti== $word
untuk== $word*
untuk pencocokan awalan; gunakan${elmt,,} == ${word,,}
untuk pencocokan tidak peka huruf besar / kecil; dll., apa pun yang[[
didukung bash .Ia bekerja dengan menentukan indeks dari larik input dan mengulanginya secara terbalik (jadi menghapus elemen tidak mengacaukan urutan iterasi). Untuk mendapatkan indeks, Anda perlu mengakses larik input berdasarkan nama, yang dapat dilakukan melalui indireksi variabel bash
x=1; varname=x; echo ${!varname} # prints "1"
.Anda tidak dapat mengakses array dengan nama seperti
aryname=a; echo "${$aryname[@]}
, ini memberi Anda kesalahan. Anda tidak dapat melakukannyaaryname=a; echo "${!aryname[@]}"
, ini memberi Anda indeks variabelaryname
(meskipun itu bukan array). Apa yang berhasilaryref="a[@]"; echo "${!aryref}"
, yang akan mencetak elemen dari arraya
, mempertahankan kutipan kata-shell dan spasi persis seperti ituecho "${a[@]}"
. Tapi ini hanya bekerja untuk mencetak elemen array, bukan untuk mencetak panjang atau indeks (aryref="!a[@]"
atauaryref="#a[@]"
atau"${!!aryref}"
atau"${#!aryref}"
, mereka semua gagal).Jadi saya menyalin array asli dengan namanya melalui bash indirection dan mendapatkan indeks dari salinannya. Untuk mengulang indeks secara terbalik, saya menggunakan for loop. Saya juga bisa melakukannya dengan mengakses indeks melalui
${!arycopy[@]}
dan membalikkannya dengantac
, yang merupakancat
memutar urutan baris input.Solusi fungsi tanpa pengaruh variabel mungkin harus terlibat
eval
, yang mungkin atau mungkin tidak aman untuk digunakan dalam situasi itu (saya tidak tahu).sumber
delete_ary_elmt "d" array
dan cetak ulang array. Anda akan melihat bahwa elemen yang salah dihapus. Menghapus elemen terakhir juga tidak akan pernah berhasil.Untuk memperluas jawaban di atas, berikut ini dapat digunakan untuk menghapus beberapa elemen dari larik, tanpa pencocokan parsial:
Ini akan menghasilkan larik yang berisi: (dua satu dua tiga tiga empat "satu enam")
sumber
Jika ada yang menemukan dirinya dalam posisi di mana mereka perlu mengingat nilai set -e atau set -x dan dapat memulihkannya, lihat inti ini yang menggunakan solusi penghapusan array pertama untuk mengelola tumpukannya sendiri:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
sumber
Jawaban parsial saja
Untuk menghapus item pertama dalam larik
Untuk menghapus item terakhir dalam larik
sumber
unset
.array0
di direktori saat ini, maka karenaarray[0]
adalah glob, itu akan diperluas terlebih dahuluarray0
sebelum perintah unset.Menggunakan
unset
Untuk menghapus elemen pada indeks tertentu, kita dapat menggunakan
unset
dan kemudian menyalin ke array lain. Hanya sajaunset
tidak diperlukan dalam kasus ini. Karenaunset
tidak menghapus elemen itu hanya menetapkan string null ke indeks tertentu dalam array.Outputnya adalah
Menggunakan
:<idx>
Kami juga dapat menghapus beberapa set elemen menggunakan
:<idx>
. Misalnya jika kita ingin menghapus elemen pertama kita dapat menggunakan:1
seperti yang disebutkan di bawah ini.Outputnya adalah
sumber
Skrip shell POSIX tidak memiliki larik.
Jadi, kemungkinan besar Anda menggunakan dialek tertentu seperti
bash
, korn shells atauzsh
.Oleh karena itu, pertanyaan Anda sampai saat ini tidak dapat dijawab.
Mungkin ini berhasil untuk Anda:
sumber
Sebenarnya, saya baru saja memperhatikan bahwa sintaks shell agak memiliki perilaku built-in yang memungkinkan rekonstruksi larik dengan mudah ketika, seperti yang diajukan dalam pertanyaan, sebuah item harus dihapus.
Perhatikan bagaimana kita membangun array menggunakan bash's
x+=()
sintaks ?Anda sebenarnya dapat menambahkan lebih dari satu item dengan itu, konten dari seluruh larik lainnya sekaligus.
sumber
http://wiki.bash-hackers.org/syntax/pe#substring_removal
Untuk melakukan elemen hapus penuh, Anda harus melakukan perintah unset dengan pernyataan if. Jika Anda tidak peduli tentang menghapus prefiks dari variabel lain atau tentang mendukung whitespace dalam larik, maka Anda dapat melepaskan tanda kutip dan melupakan untuk loop.
Lihat contoh di bawah untuk beberapa cara berbeda untuk membersihkan larik.
Keluaran
Semoga membantu.
sumber
Di ZSH ini sangat mudah (perhatikan ini menggunakan sintaks yang lebih kompatibel dengan bash daripada yang diperlukan jika memungkinkan untuk kemudahan pemahaman):
Hasil:
sumber
Ada juga sintaks ini, misalnya jika Anda ingin menghapus elemen ke-2:
yang merupakan gabungan dari 2 tab. Yang pertama dari indeks 0 ke indeks 1 (eksklusif) dan yang ke-2 dari indeks 2 sampai akhir.
sumber
Yang saya lakukan adalah:
BAM, item itu dihapus.
sumber
array=('first item' 'second item')
.Ini adalah solusi cepat dan kotor yang akan berfungsi dalam kasus sederhana tetapi akan rusak jika (a) ada karakter khusus regex di
$delete
, atau (b) ada spasi sama sekali di item mana pun. Dimulai dengan:Hapus semua entri yang sama persis
$delete
:menghasilkan
echo $array
-> pippo, dan pastikan itu adalah array:echo $array[1]
-> pippofmt
agak kabur:fmt -1
membungkus di kolom pertama (untuk meletakkan setiap item di barisnya sendiri. Di situlah masalah muncul dengan item dalam spasi.)fmt -999999
membukanya kembali ke satu baris, meletakkan kembali spasi di antara item. Ada cara lain untuk melakukannya, sepertixargs
.Tambahan: Jika Anda hanya ingin menghapus kecocokan pertama, gunakan sed, seperti yang dijelaskan di sini :
sumber
Bagaimana dengan sesuatu seperti:
sumber
Untuk menghindari konflik dengan indeks array menggunakan
unset
- lihat https://stackoverflow.com/a/49626928/3223785 dan https://stackoverflow.com/a/47798640/3223785 untuk informasi lebih lanjut - menetapkan kembali array untuk dirinya sendiri:ARRAY_VAR=(${ARRAY_VAR[@]})
.[Ref .: https://tecadmin.net/working-with-array-bash-script/ ]
sumber
sumber