Dalam semua shell yang saya sadari, rm [A-Z]*
menghapus semua file yang dimulai dengan huruf besar, tetapi dengan bash ini menghapus semua file yang dimulai dengan huruf.
Karena masalah ini ada di Linux dan Solaris dengan bash-3 dan bash-4, itu tidak bisa berupa bug yang disebabkan oleh pencocokan pola kereta di libc atau definisi lokal yang tidak terkonfigurasi.
Apakah ini perilaku aneh dan berisiko yang dimaksudkan atau ini hanya bug yang ada yang tidak diperbaiki sejak bertahun-tahun?
locale
output? Saya tidak dapat mereproduksi ini (touch foo; echo [A-Z]*
menampilkan pola literal, bukan "foo", di direktori yang kosong).# echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*
A b B z ZABZJawaban:
LC_COLLATE
adalah variabel yang menentukan urutan pemeriksaan yang digunakan saat menyortir hasil ekspansi pathname, dan menentukan perilaku ekspresi rentang, kelas ekivalensi, dan menyusun urutan dalam ekspansi pathname dan pencocokan pola.Pertimbangkan yang berikut ini:
Perhatikan ketika perintah
echo [a-z]
dipanggil, output yang diharapkan adalah semua file dengan karakter huruf kecil. Juga, denganecho [A-Z]
, file dengan karakter huruf besar akan diharapkan.Pengumpulan standar dengan lokal seperti
en_US
memiliki urutan berikut:a
danz
(dalam[a-z]
) adalah SEMUA huruf besar, kecuali untukZ
.A
danZ
(dalam[A-Z]
) adalah SEMUA huruf kecil, kecuali untuka
.Lihat:
Jika Anda mengubah
LC_COLLATE
variabel menjadiC
seperti yang diharapkan:Jadi, ini bukan bug , ini masalah pengumpulan .
Alih-alih rentang ekspresi Anda dapat menggunakan kelas karakter yang didefinisikan POSIX , seperti
upper
ataulower
. Mereka juga bekerja denganLC_COLLATE
konfigurasi yang berbeda dan bahkan dengan karakter beraksen :sumber
tr
jadi ini yang saya periksa dulu.LC_COLLATE
yang juga didokumentasikan dalam manual.[A-Z]
dibash
cocokkan semua elemen penyusun (karakter tetapi panggilan juga menjadi urutan karakter sepertiDsz
di lokal Hongaria) yang mengurutkan setelahA
dan mengurutkan sebelumnyaZ
. Di tempat Anda,c
mungkin di antara B dan C.Jadi
c
atauz
akan dicocokkan dengan[A-Z]
, tetapi tidakẐ
ataua
.Di C locale, urutannya adalah:
Jadi
[A-Z]
akan cocokA
,B
,C
,Z
, tapi tidakÇ
dan masih tidakẐ
.Jika Anda ingin mencocokkan huruf besar (dalam skrip apa pun), Anda dapat menggunakannya
[[:upper:]]
. Tidak ada jalan masukbash
untuk hanya mencocokkan huruf besar dalam skrip latin (kecuali dengan mendaftar secara individual).Jika Anda ingin mencocokkan huruf
A
keZ
bahasa Inggris tanpa diakritik, Anda dapat menggunakan[A-Z]
atau[[:upper:]]
tetapi diC
lokal (dengan asumsi data tidak dikodekan dalam set karakter seperti BIG5 atau GB18030 yang memiliki beberapa karakter yang pengkodeannya berisi pengkodean surat-surat itu) atau daftar secara individual ([ABCDEFGHIJKLMNOPQRSTUVWXYZ]
).Perhatikan bahwa ada beberapa variasi antara cangkang.
Untuk
zsh
,bash -O globasciiranges
(opsi yang dinamai aneh diperkenalkan di bash-4.3),schily-sh
danyash
,[A-Z]
cocok dengan karakter yang titik kodenya antaraA
dan dariZ
, jadi akan sama dengan perilakubash
di lokal C.Untuk abu, mksh dan cangkang kuno, sama seperti di
zsh
atas tetapi terbatas pada rangkaian byte tunggal. Yaitu, di lokal UTF-8 misalnya,[É-Ź]
tidak akan cocokÓ
, tetapi karena itu[<c3><89>-<c5><b9>]
, itu akan cocok dengan nilai byte 0x89 hingga 0xc5!ksh93
berperilaku sepertibash
kecuali bahwa itu memperlakukan rentang kasus khusus yang ujungnya dimulai dengan huruf kecil atau huruf besar. Dalam hal ini, itu hanya cocok pada elemen penyusun yang mengurutkan antara kedua ujungnya, tetapi itu adalah (atau karakter pertama mereka untuk elemen penyusun multi-karakter) juga huruf kecil (atau masing-masing huruf besar). Jadi[A-Z]
akan cocokÉ
, tetapi tidak padae
sepertie
halnya antaraA
danZ
tetapi tidak huruf besar sepertiA
danZ
.Untuk
fnmatch()
pola (seperti dalamfind -name '[A-Z]'
) atau ekspresi reguler sistem (seperti dalamgrep '[A-Z]'
), itu tergantung pada sistem dan lokal. Sebagai contoh, pada sistem GNU di sini,[A-Z]
tidak cocokx
dien_GB.UTF-8
lokal, tetapi itu cocok dith_TH.UTF-8
satu. Tidak jelas bagi saya informasi apa yang digunakannya untuk menentukan hal itu, tetapi tampaknya berdasarkan tabel pencarian yang berasal dari data lokal LC_COLLATE ).Semua perilaku diizinkan oleh POSIX karena POSIX membiarkan perilaku rentang tidak ditentukan di lokal selain dari C locale. Sekarang kita dapat berdebat tentang manfaat dari setiap pendekatan.
bash
Pendekatan banyak masuk akal dengan[C-G]
, kami ingin karakter di antaraC
danG
. Dan menggunakan urutan pengguna untuk menentukan apa yang ada di antara keduanya adalah pendekatan yang paling logis.Sekarang, masalahnya adalah itu menghancurkan harapan banyak orang, terutama orang-orang yang terbiasa dengan perilaku tradisional pra-Unicode, bahkan sebelum hari internasionalisasi. Sementara dari pengguna normal, masuk akal jika
[C-I]
menyertakanh
karenah
surat itu antaraC
danI
dan yang[A-g]
tidak termasukZ
, itu masalah yang berbeda bagi orang-orang yang berurusan dengan ASCII hanya selama beberapa dekade.Itu
bash
perilaku juga berbeda dari[A-Z]
pencocokan berbagai dalam alat GNU lain seperti di GNU ekspresi reguler (seperti dalamgrep
/sed
...) ataufnmatch()
seperti dalamfind -name
.Ini juga berarti bahwa apa yang
[A-Z]
cocok bervariasi dengan lingkungan, dengan OS dan dengan versi OS. Fakta yang[A-Z]
cocok dengan Á tetapi tidak Ź juga tidak optimal.Untuk
zsh
/yash
, kami menggunakan urutan penyortiran yang berbeda. Alih-alih mengandalkan gagasan pengguna tentang urutan karakter, kami menggunakan nilai kode titik karakter. Itu memiliki manfaat karena mudah dipahami, tetapi dari sudut pandang praktis, di luar ASCII, itu tidak terlalu berguna.[A-Z]
cocok dengan 26 huruf besar Inggris-Inggris,[0-9]
cocok dengan angka desimal. Ada poin kode di Unicode yang mengikuti urutan beberapa huruf tetapi itu tidak digeneralisasi dan tidak dapat digeneralisasi karena orang yang berbeda menggunakan skrip yang sama tidak harus menyetujui urutan huruf.Untuk shells tradisional dan mksh, dash, itu rusak (sekarang kebanyakan orang menggunakan karakter multi-byte), tetapi terutama karena mereka belum memiliki dukungan multi-byte. Menambahkan dukungan multi-byte ke shell like
bash
danzsh
telah menjadi upaya besar dan masih berlangsung.yash
(shell Jepang) pada awalnya dirancang dengan dukungan multi-byte sejak awal.Pendekatan ksh93 bermanfaat untuk konsisten dengan ekspresi reguler atau fnmatch sistem () (atau setidaknya tampak setidaknya pada sistem GNU). Di sana, itu tidak melanggar harapan beberapa orang karena
[A-Z]
tidak termasuk huruf kecil,[A-Z]
termasukÉ
(dan Á, tetapi tidak Ź). Itu tidak konsisten dengansort
atau umumnyastrcoll()
memesan.sumber
mksh
(keduanya berasal dari pdksh).posh -c $'case Ó in [É-Ź]) echo yes; esac'
tidak mengembalikan apa pun.sort
karenabash
gumpalan didasarkan pada urutan jenis karakter. Saat ini saya tidak memiliki akses ke versi lama seperti itubash
, tetapi saya dapat memeriksanya nanti. Apakah itu berbeda?\xFF
ada byte 0xFF, bukan karakter U + 00FF (ÿ
itu sendiri dikodekan sebagai 0xC3 0xBF).\xFF
sendiri tidak membentuk karakter yang valid jadi saya tidak bisa melihat mengapa itu harus dicocokkan dengan[É-Ź]
.Ini dimaksudkan dan didokumentasikan dalam
bash
dokumentasi, bagian pencocokan pola . Ekspresi rentang[X-Y]
akan menyertakan karakter apa pun di antaraX
danY
menggunakan urutan susunan dan rangkaian karakter lokal saat ini:Anda dapat melihat,
b
diurutkan di antaraA
danZ
dien_US.utf8
lokal.Anda memiliki beberapa pilihan untuk mencegah perilaku ini:
atau aktifkan
globasciiranges
(dengan bash 4.3 ke atas):sumber
Saya mengamati perilaku ini pada instance Amazon EC2 baru. Karena OP tidak menawarkan MCVE , saya akan memposting satu:
Jadi, tidak memiliki
LC_*
set saya memimpin bash 4.1.2 (1) - rilis di Linux untuk menghasilkan perilaku yang aneh. Saya andal dapat beralih perilaku aneh dengan mengatur dan menghapus variabel lokal masing-masing. Tidak mengejutkan, perilaku ini tampak konsisten melalui ekspor:Sementara saya melihat bash berperilaku sebagai Stéphane "Shellshock" jawab Chazelas , saya pikir dokumentasi bash tentang pencocokan pola adalah buggy:
Saya membaca kalimat itu (penekanan milik saya) sebagai "jika variabel lokal yang relevan tidak disetel, maka bash akan default ke lokal C". Bash tampaknya tidak melakukan itu. Alih-alih tampaknya menjadi default ke lokal tempat karakter diurutkan dalam urutan kamus dengan lipat diakritik:
Saya pikir itu akan baik bagi bash untuk mendokumentasikan bagaimana perilakunya ketika
LC_*
(secara khususLC_CTYPE
danLC_COLLATE
) tidak ditentukan. Tetapi sementara itu, saya akan membagikan beberapa kebijaksanaan :dan
Pembaruan Berdasarkan pada komentar G-Man, mari kita lihat lebih dalam apa yang terjadi:
Ah, ha! Itu menjelaskan pemeriksaan yang terlihat sebelumnya. Mari kita hapus semua variabel lokal:
Itu dia. Sekarang bash beroperasi secara konsisten sehubungan dengan dokumentasi pada sistem Linux ini. Jika salah satu variabel lokal ditetapkan (
LANGUAGE
,LANG
,LC_COLLATE
,LC_CTYPE
,LC_ALL
, dll) maka Bash menggunakan mereka sesuai dengan yang manual. Kalau tidak, bash jatuh kembali ke C.The Wooledge pesta FAQ telah mengatakan ini:
Jadi masalah yang tampak, baik dalam operasi dan dokumentasi, dapat dijelaskan dengan melihat jumlah total semua variabel penggerak lokal.
sumber
C
lokal, ini adalah bug.env | grep LANG
atauecho "$LANG"
.LANG
. Dengan petunjuk itu, semua dijelaskan.Lokal dapat mengubah karakter apa yang cocok dengan
[A-Z]
. Menggunakanuntuk menghilangkan pengaruh. (Saya menggunakan subkulit untuk melokalisasi perubahan).
sumber
export LC_ALL=C
dulu.Seperti yang telah dikatakan, ini adalah masalah "menyusun urutan".
Rentang az dapat berisi huruf besar di beberapa lokal:
Solusi yang benar karena bash 4.3 adalah mengatur opsi
globasciiranges
:untuk membuat bash bertindak seolah-olah
LC_COLLATE=C
telah diatur dalam rentang bola global .sumber
Tampaknya saya menemukan jawaban yang tepat untuk pertanyaan saya sendiri:
Bash buggy karena tidak mengelola lokal itu sendiri. Jadi pengaturan LC_ * dalam proses bash tanpa efek dalam proses shell itu.
Jika Anda mengatur LC_COLLATE = C dan kemudian memulai bash lainnya, globbing berfungsi seperti yang diharapkan dalam proses bash baru.
sumber
export
dengan benar.