Bash string ganti beberapa karakter dengan satu

8

Saya mengganti, dari judul umpan, semua karakter kecuali huruf dan digit dengan tanda hubung untuk menggunakan hasilnya sebagai nama file aman untuk sistem file apa pun:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

Namun saya ingin menyingkat semua strip berulang dengan satu seperti Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

Saya menemukan saya dapat mencapainya dengan menggunakan dua pass substitusi:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

Saya pikir saya bisa melakukannya dalam sekali jalan seperti:

$ echo ${t//[^A-Za-z0-9]+/-}

tetapi tidak berhasil.

Ada petunjuk?

Catatan: Saya tidak ingin menggunakan sedalat lain

neurino
sumber

Jawaban:

8

Anda memerlukan sesuatu yang lebih kuat daripada wildcard shell tradisional. Di bash, setel extglobopsi, yang memberi Anda akses ke ekspresi reguler dalam pola glob melalui sintaks yang tidak biasa yang diwarisi dari ksh.

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih, ada komentar dari fered di bawah jawaban jw013 dengan solusi ini. Beberapa info tentang kompatibilitas dengan shell lain dari sintaks ini? Saya tidak terlalu khawatir tentang hal itu, hanya untuk mengetahui lebih banyak shoptdan cangkang mana yang mendukungnya.
neurino
@neurino shoptkhusus untuk bash. Sintaks pola yang diaktifkan selalu tersedia di semua varian ksh. Di zsh, sintaks ini harus diaktifkan dengan setopt ksh_glob. POSIX tidak memiliki fitur seperti itu, wildcard-nya kurang kuat daripada regexps. Kerang selain bash / ksh / zsh, yang dalam praktiknya sebagian besar berarti abu saat ini, cenderung menempel pada wildcard POSIX.
Gilles 'SO- stop being evil'
baik, saat ini saya lebih suka lebih kompatibilitas dan fleksibilitas dengan sedikit lebih overhead: echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'. Saya menerima jawaban Anda karena persis melakukan apa yang ditanyakan.
neurino
@neurino Jika Anda ingin portabilitas untuk kerang lainnya, maka Anda dapat pergi dengan jawaban glenn jackman ini . By the way, perhatikan bahwa ${var/PATTERN/REPLACEMENT}konstruksinya juga spesifik untuk ksh / bash / zsh.
Gilles 'SO- stop being evil'
Saya lebih suka sedkarena saya tahu lebih baik sintaks dan perilakunya, saya dapat dengan mudah menambahkan pernyataan untuk menghapus garis start / trailing, saya tidak perlu peduli dengan \nchar. Apakah sedcara yang tersedia lebih sedikit daripada tr?
neurino
7

tr adalah alat yang baik untuk pekerjaan ini

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}
glenn jackman
sumber
Terima kasih, +1, saya tidak pernah ingat tentang tr... Namun saya mencoba menyelesaikannya di Bash, kalau tidak saya akan pergi dengan sed:echo "$t" | sed -r 's/[^A-Za-z0-9]+/-/g'
neurino
Turut memilih karena bertentangan denganNote: I don't want to go with sed or other tools
Paul Calabro
3

Jika Anda ingin tetap menggunakan bash murni, Anda harus puas dengan solusi dua arah. Pergantian string bash menggunakan gumpalan , seperti dalam ekspansi pathname, dan bukan ekspresi reguler. Satu-satunya karakter khusus dalam gumpalan yang *, ?dan [], yang kasar setara dalam ekspresi reguler .*, .dan []. Lihatlah wiki Wooledge dan bagian halaman manual dan untuk info lebih lanjut.bash(1)Parameter ExpansionPathname Expansion

Sama seperti komentar, ekspansi dua-pass dalam bash murni masih cenderung lebih cepat daripada mencoba melakukan hal yang sama dengan menjalankan program eksternal, jadi saya tidak akan terlalu khawatir tentang hal itu.

jw013
sumber
Terima kasih, saya akan memeriksa tautannya. Kekhawatiran saya adalah saya harus melakukan pekerjaan ini lebih dari satu kali di seluruh skrip sehingga satu-satunya kekhawatiran saya adalah memiliki kode yang sama berulang-ulang dengan kompromi keterbacaan. Pokoknya saya datang dengan solusi sopan yang akan saya posting. Cheers
neurino
Anda bisa meletakkan kode itu dalam suatu fungsi untuk menghindari kode berulang.
jw013
Ini yang saya lakukan tetapi, seperti yang Anda tahu, fungsi bash tidak dapat mengembalikan string ... atau, setidaknya, itulah yang saya pikirkan sebelum 10 menit yang lalu :)
neurino
4
Berikut adalah beberapa contoh dengan do-dan-jangan-s - Bash Extended Globbing .. Untuk contoh di atas, itu akan menjadi:shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}"
Peter.O
1
@fered: terima kasih, sangat menarik, saya akan memeriksanya. URL tautan Anda memiliki char tambahan dan mengembalikan 404, yang berfungsi adalah Bash Extended Globbing
neurino