Ekspresi braket (tanpa rentang) cocok dengan karakter tak terduga dalam bash


20

Saya menggunakan bash di Linux. Saya mendapatkan kesuksesan dari pernyataan if berikut ini, tetapi tidakkah ini menghasilkan kode gagal?

if [[  = [⅕⅖⅗] ]] ; then echo yes ; fi

Alun-alun BUKAN sama dengan salah satu karakter, jadi saya tidak melihat mengapa saya mendapatkan kode sukses.

Penting bagi saya untuk menyimpan kurung ganda dalam kasus saya.

Apakah ada cara lain untuk melakukan rentang dalam skenario ini, atau apa saran lainnya?


2
Mungkin konsekuensi dari semua karakter yang memiliki urutan penyortiran yang tidak ditentukan di lokal Anda (dan dengan demikian menyortir yang sama). Lihat diskusi terkait yang sedang berlangsung di grup Austin . Ubah lokal ke C untuk memperbaikinya .
Stéphane Chazelas

1
Maaf, Ctidak akan dilakukan di sini karena ini bukan karakter byte tunggal. C.UTF-8akan dilakukan jika tersedia.
Stéphane Chazelas

11
Selamat, Anda berhasil memanggil Stéphane menggunakan utas Grup Austin pada pertanyaan pertama Anda. Itu harus bernilai setidaknya ⅗ dari Internet. Atau ⅘ atau bahkan ■ Internet, karena tampaknya semuanya sama. Selamat datang di Unix & Linux , dan tolong terus ajukan pertanyaan menarik.
derobert

Jawaban:


29

Itu konsekuensi dari karakter-karakter yang memiliki urutan penyortiran yang sama.

Anda juga akan memperhatikan itu

sort -u << EOF




EOF

mengembalikan hanya satu baris.

Atau itu:

expr  = 

mengembalikan true (seperti yang disyaratkan oleh POSIX).

Sebagian besar lokal yang dikirimkan dengan sistem GNU memiliki sejumlah karakter (dan bahkan urutan karakter (susun urutan)) yang memiliki urutan penyortiran yang sama. Dalam kasus orang-orang ■ itu, itu karena urutannya tidak ditentukan, dan karakter-karakter yang urutannya tidak didefinisikan pada akhirnya memiliki urutan penyortiran yang sama dalam sistem GNU. Ada karakter yang secara eksplisit didefinisikan sebagai memiliki urutan penyortiran yang sama seperti Ș dan Ş (meskipun tidak ada logika nyata atau konsistensi tentang bagaimana hal itu dilakukan).

Itulah sumber perilaku yang cukup mengejutkan dan palsu. Saya telah mengangkat masalah ini baru-baru ini di milis Austin (badan di belakang POSIX dan Spesifikasi UNIX Tunggal) dan diskusi masih berlangsung hingga 2015-04-03.

Dalam hal ini, apakah [y]harus cocok dengan di xmana xdan ymengurutkan yang sama tidak jelas bagi saya, tetapi karena ekspresi braket dimaksudkan untuk mencocokkan elemen penyusun, itu menunjukkan bahwa bashperilaku tersebut diharapkan.

Bagaimanapun, saya kira [⅕-⅕]atau setidaknya [⅕-⅖]harus cocok .

Anda akan melihat bahwa berbagai alat berperilaku berbeda. ksh93 berperilaku seperti bash, GNU grepatau sedtidak. Beberapa cangkang lain memiliki perilaku yang berbeda, beberapa yashbahkan lebih buggy.

Untuk memiliki perilaku yang konsisten, Anda memerlukan lokal di mana semua karakter diurutkan secara berbeda. C locale adalah yang khas. Namun karakter yang ditetapkan di C locale pada kebanyakan sistem adalah ASCII. Pada sistem GNU, Anda biasanya memiliki akses ke C.UTF-8lokal yang dapat digunakan untuk bekerja pada karakter UTF-8.

Begitu:

(export LC_ALL=C.UTF-8; [[  = [⅕⅖⅗] ]])

atau standar yang setara:

(export LC_ALL=C.UTF-8
 case  in ([⅕⅖⅗]) true;; (*) false; esac)

harus mengembalikan false.

Alternatif lain adalah dengan menetapkan hanya LC_COLLATEuntuk C yang akan bekerja pada sistem GNU, tetapi tidak harus pada yang lain di mana ia bisa gagal untuk menentukan urutan penyortiran karakter multi-byte.


Satu pelajaran tentang hal itu adalah bahwa kesetaraan tidak sejelas gagasan seperti yang diharapkan orang ketika membandingkan string. Kesetaraan mungkin berarti, dari yang paling ketat hingga yang paling tidak ketat.

  1. Jumlah byte dan konstituen byte yang sama memiliki nilai yang sama.
  2. Jumlah karakter yang sama dan semua karakter adalah sama (misalnya, merujuk pada codepoint yang sama di rangkaian karakter saat ini).
  3. Kedua string memiliki urutan pengurutan yang sama sesuai dengan algoritma collation lokal (yaitu, a <b atau b> a tidak benar).

Sekarang, untuk 2 atau 3, yang mengasumsikan kedua string berisi karakter yang valid. Dalam UTF-8 dan beberapa pengkodean lainnya, beberapa urutan byte tidak membentuk karakter yang valid.

1 dan 2 tidak harus sama karena itu, atau karena beberapa karakter mungkin memiliki lebih dari satu kemungkinan penyandian. Itu biasanya kasus pengkodean stateful seperti ISO-2022-JP di mana Adapat dinyatakan sebagai 41atau 1b 28 42 41( 1b 28 42menjadi urutan untuk beralih ke ASCII dan Anda dapat memasukkan sebanyak yang Anda inginkan, yang tidak akan membuat perbedaan), meskipun saya tidak akan mengharapkan jenis-jenis penyandian itu masih digunakan, dan alat-alat GNU setidaknya secara umum tidak bekerja dengan benar.

Berhati-hatilah karena sebagian besar utilitas non-GNU tidak dapat menangani nilai 0 byte (karakter NUL di ASCII).

Mana yang definisi yang digunakan tergantung pada utilitas dan utilitas implementasi atau versi. POSIX tidak 100% jelas tentang hal itu. Di C locale, ketiganya setara. Di luar YMMV itu.


Kasus umum lain di mana 1 dan 2 berbeda adalah dalam Unicode dengan hal-hal seperti menggabungkan karakter.
Gilles 'SO- stop being evil'

@Gilles, menggabungkan karakter adalah karakter mereka sendiri. Kombinasi membentuk graphem / sel, tetapi masih terbentuk dari beberapa karakter. é (U + 00E9) dan é (e diikuti oleh U + 0301) adalah graphem yang sama, tetapi dua urutan karakter yang berbeda (setidaknya dari sudut pandang API POSIX). Dengan 1 dan 2, mereka akan berbeda. Pada 3, mereka dapat dianggap sama jika U + 0301 memiliki semua bobot kolasi yang ditetapkan ke "IGNORE", tapi itu umumnya tidak terjadi karena orang umumnya ingin memutuskan urutan diakritik.
Stéphane Chazelas

Biasanya diinginkan untuk mempertimbangkan édan menjadi string yang sama, tetapi tidak e. Gagasan POSIX tentang susunan kolase jarang benar, terlalu berat berdasarkan karakter dan tidak menjelaskan cara penyortiran string yang paling umum (misalnya kamus Prancis tidak menggunakan urutan leksikografis untuk mengurutkan kata-kata: mereka melakukan leksikografi pertama dengan aksen diabaikan dan kemudian gunakan aksen untuk memutuskan ikatan).
Gilles 'SO- berhenti menjadi jahat'

@Gilles, ya. Itu sebabnya saya mengatakan bahwa karakter-karakter yang memiliki urutan penyortiran yang sama (disengaja) di glibc locales tidak masuk akal. É vs é biasanya ditangani dengan melakukan beberapa transformasi pada string terlebih dahulu seperti dekomposisi kanonik (mirip dengan mengkonversi ke huruf kecil terlebih dahulu ketika Anda ingin melakukan penyortiran / pencocokan case-insensitive). Lihat juga panduan ICU untuk referensi yang bagus tentang masalah ini.
Stéphane Chazelas

@Gilles, bobot dalam algoritma collation lokal POSIX dapat melakukan pengurutan kamus Prancis. Begitulah cara kerja bobot. Lewat pertama menggunakan bobot utama (di mana e dan é (dan E dan É) memiliki yang sama dan aksen akut kombinasi diabaikan) lintasan kedua (jika sama) memeriksa aksen, kapitalisasi pass ketiga ...
Stéphane Chazelas

-3

Anda salah melakukannya, =dan ==tidak sama.

Coba contoh ini:

if [[ "■" == "[⅕⅖⅗]" ]] ; then echo yes ; else echo no ; fi

if [[ "1" == "1" ]] ; then echo yes ; else echo no ; fi

if [[ "■" == "■" ]] ; then echo yes ; else echo no ; fi

1
Itu tidak benar. POSIX menentukan bahwa operator =harus digunakan untuk memeriksa kesetaraan. Masalahnya adalah kutipan yang hilang, bukan operator.
scai

1
Juga man bashmengatakan di [[bagian: "Operator = setara dengan ==."
michas

1
@Cai, POSIX tidak menentukan [[...]]operator. Dan = dan == sama di shell jika itu diterapkan (ksh / bash / zsh) dan untuk pencocokan pola, bukan kesetaraan.
Stéphane Chazelas

Ketika membandingkan dengan suatu pola, polanya tidak harus dikutip, jika tidak diambil sebagai string literal, maka "tidak" dalam tes pertama.
xhienne
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.