Sensitivitas case pada globbing braket persegi

10

Biasanya, bash globbing peka terhadap huruf besar-kecil:

$ echo c*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo C*
CarePackage.md ChocRippleCake.md Clips

Menggunakan tanda kurung siku tampaknya tidak mengubah ini:

$ echo [c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C]*
CarePackage.md ChocRippleCake.md Clips

Itu masih tidak mengubahnya jika tanda hubung digunakan:

$ echo [c-c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C-C]*
CarePackage.md ChocRippleCake.md Clips

Tetapi surat-surat itu diselingi:

$ echo [B-C]*
CarePackage.md casefix.pike cdless chalices.py charconv.py chocolate.pike ChocRippleCake.md circum.py clip.pike Clips cpustats.pike crop.pike cwk2txt.py
$ echo [b-c]*
beehive-anthem.txt bluray2mkv.pike branch branchcleanup.pike burdayim.pike casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py

Ini menunjukkan bahwa tanda hubung menggunakan urutan lokal, "AaBbCcDd". Jadi: apakah ada cara untuk glob untuk semua file yang dimulai dengan huruf besar?

rosuav
sumber
3
Catat juga gotcha yang [AZ] cocok dengan setiap huruf kecil kecuali 'z'!
PJTraill

Jawaban:

12

Di bash versi 4.3 dan yang lebih baru, ada opsi shopt bernama globasciiranges:

Menurut halaman manual shopt builtin gnu :

globasciiranges
Jika diatur, rentang ekspresi yang digunakan dalam ekspresi braket pencocokan pola (lihat Pencocokan Pola) berperilaku seolah-olah di lokal C tradisional saat melakukan perbandingan. Artinya, urutan penyusunan lokal saat ini tidak diperhitungkan, jadi 'b' tidak akan menyusun antara 'A' dan 'B', dan karakter ASCII huruf besar dan huruf kecil akan disusun bersama.

Hasilnya kamu bisa

$ shopt -s globasciiranges 
$ echo [A-Z]*

Gunakan shopt -uuntuk menonaktifkan.

Cara lain adalah mengubah lokal ke C. Anda dapat melakukan ini sementara menggunakan subkulit:

$ ( LC_ALL=C ; printf '%s\n' [A-Z]*; )

Anda akan mendapatkan hasil yang Anda butuhkan, dan ketika sub shell selesai, lokal shell utama Anda tetap tidak berubah menjadi apa pun sebelumnya.

Alternatif lain adalah alih-alih [A-Z]menggunakan ekspansi brace {A..Z}bersama dengan nullglobopsi bash shopt.

Dengan mengaktifkan nullglobopsi, jika suatu pola tidak cocok selama ekspansi pathname, string null dikembalikan sebagai ganti pola itu sendiri.
Akibatnya yang ini akan berfungsi seperti yang diharapkan:

$ shopt -s nullglob;printf '%s\n' {A..Z}*
George Vasiliou
sumber
2
Sempurna terima kasih. Saya tidak dapat menggunakan [[:upper:]]karena saya benar-benar ingin hanya bagian dari alfabet, tetapi ini berfungsi.
rosuav
1
@rosuav Selamat datang. Periksa juga alternatif sub shell.
George Vasiliou
“Jika diaktifkan sama dengan C locale” - maksud Anda itu memengaruhi lokal yang digunakan untuk globbing dan tidak ada yang lain? (Tautan referensi akan sangat membantu - yang terbaik yang dapat saya temukan adalah gnu.org/software/bash/manual/html_node/Pattern-Matching.html , tapi saya lebih suka daftar semua opsi shell, tetapi globasciiranges tidak ada) dari gnu.org/software/bash/manual/html_node/… ; juga pertanyaan unix.stackexchange.com/questions/227070/… menangani masalah ini secara ekstensif.) Juga dari versi 4.3.
PJTraill
@ PjTrail Lihat edit saya dengan tautan referensi ke semua opsi shopt. Anda juga dapat berjalan man bashdi terminal Anda dan mencari (menggunakan /) untuk globasciiranges.
George Vasiliou
Tidak akan LC_ALL=C printf '%s\n' [A-Z]*bekerja untuk solusi kedua Anda - tanpa subkulit? BTW: ada salah ketik:, nullblogtapi terlalu sedikit karakter untuk saya perbaiki.
Joe
5

Anda dapat menulis semua huruf besar dengan baik seperti:

[ABCDEFGHIJKLMNOPQRSTUVWXYZ]*

atau gunakan dapat menggunakan kelas karakter bernama [:upper:]untuk mewakili semua huruf besar di saat ini Anda locale:

[[:upper:]]*

Seperti yang telah Anda perhatikan, saat menggunakan rentang seperti [B-C]huruf besar dan kecil untuk karakter alfabet yang sama sedang diatur secara berdampingan (sesuai dengan urutan susunan huruf locale).

heemayl
sumber
3

Termasuk karakter "tidak intuitif" dalam rentang karakter, seperti termasuk huruf kecil dalam rentang yang batas-batasnya adalah huruf besar, karena LC_COLLATEpengaturan lokal. LC_COLLATEseharusnya menunjukkan urutan penyortiran, tetapi itu melakukan pekerjaan yang buruk (menyortir string lebih kompleks daripada apa yang dapat dilakukan lokal) dan Anda lebih baik tanpanya. Saya sarankan untuk menghapus LC_COLLATEdari pengaturan lokal Anda. Jika Anda pengaturan sedang LANG, atau LANGUAGE, jangan lakukan itu dan mengatur hanya yang Anda perlu: LC_CTYPE, LC_MESSAGES, LC_TIME.

Untuk latar belakang lebih lanjut tentang lokal, lihat Apa yang harus saya atur lokal saya dan apa implikasi melakukannya? dan atur LC_ * tetapi tidak LC_ALL

Untuk mendapatkan hasil yang andal dalam skrip terlepas dari pengaturan pengguna, setel LC_ALL=C.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
0

Set:

shopt -u nocaseglob

Dari halaman bash man:

>     nocaseglob
>         If  set,  bash matches filenames in a case-insensitive
>         fashion when performing pathname expansion (see Pathname
>          Expansion above).

Jika Anda mengatur 'globasciiranges', saya tidak tahu apa yang akan terjadi pada karakter non-ascii seperti utf-8

Udi
sumber
0

echo [cC] * harus melakukan apa yang Anda inginkan, sama halnya [A-Za-z] *

Saya di sini karena globbing pada sistem saya baru saja berhenti menjadi case sensitif, jadi banyak skrip saya tidak lagi berfungsi sebagaimana mestinya :-(

pengguna208007
sumber
Itu kebalikan dari apa yang saya lihat. Tetapi periksa jawaban lain untuk saran.
rosuav