[AZ] neden küçük harfleri bash ile eşleştiriyor?


43

Farkında olduğum tüm kabuklarda rm [A-Z]*büyük harfle başlayan tüm dosyaları kaldırır, ancak bash ile harf ile başlayan tüm dosyaları kaldırır.

Bu sorun, bash-3 ve bash-4 ile Linux ve Solaris'te bulunduğundan, libc'deki bir hatalı kalıp eşleştiricisinin neden olduğu bir hata veya yanlış yapılandırılmış bir yerel ayar tanımı olamaz.

Bu garip ve riskli davranış amaçlanmış mıdır yoksa bu, uzun yıllar boyunca düzeltilmemiş olan bir hatadır mı?


3
Neyi localeçıktı? Bunu çoğaltamıyorum ( touch foo; echo [A-Z]*aksi takdirde boş bir dizine değişmez kalıbı "foo" değil, çıktı olarak verir).
chepner

4
Kaç kişinin kendileri için işe yaradığını söylediğini veya LC_COLLATE'in bunu nasıl etkilediğine dair örnekler verdiğimizi düşünürsek, belki de sorunuzu tam olarak sorduğunuz senaryoyu gösteren örnek bir bash oturumu eklemek için sorunuzu düzenleyebilirsiniz. Lütfen kullandığınız bash sürümünü ekleyin.
Kenster

Buradaki tüm metni okuduysanız, hangi bash sürümünü kullandığımı ve sorumu zaten çözüme ulaştırdığımdan beri ne yaptığımı bileceksiniz. Çözümü tekrarlamama izin verin: bash kendi yerel ayarını yönetmiyor, böylece LC_COLLATE ayarı yeni ortamla başka bir bash işlemine başlayana kadar hiçbir şeyi değiştirmiyor.
schily

1
Ayrıca bakınız (gerekir) LC_COLLATE karakter aralıklarını etkiler mi? (ama bu soru özellikle bash ile ilgili değildi)
Gilles 'ÇÖZÜM'

"LC_COLLATE ayarı, yeni ortamla başka bir bash işlemine başlamadan hiçbir şeyi değiştirmez." Bu Solaris'te bash-4 ile gördüğüm davranışlarla uyuşmuyor. Çalışan kabuktaki davranışı değiştiriyor. # echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*A b B z ZABZ
BowlOfRed

Yanıtlar:


68

[Az] gibi aralık ifadeleri kullanırken, LC_COLLATE ayarına bağlı olarak diğer durumun harflerinin de dahil edilebileceğini unutmayın.

LC_COLLATE yol adı genişletme sonuçlarını sıralarken kullanılan harmanlama sırasını belirleyen ve yol ifadesi genişletme ve desen eşleştirme içindeki aralık ifadeleri, denklik sınıfları ve harmanlama dizilerinin davranışını belirleyen bir değişkendir.


Aşağıdakileri göz önünde bulundur:

$ touch a A b B c C x X y Y z Z
$ ls
a  A  b  B  c  C  x  X  y  Y  z  Z
$ echo [a-z] # Note the missing uppercase "Z"
a A b B c C x X y Y z
$ echo [A-Z] # Note the missing lowercase "a"
A b B c C x X y Y z Z

Komut echo [a-z]çağrıldığında, beklenen çıktı küçük harf karakterli tüm dosyalar olacaktır. Ayrıca, echo [A-Z]büyük harf karakterli dosyalar beklenir.


en_USAşağıdaki sıralamaya sahip yerel ayarlarla standart harmanlamalar :

aAbBcC...xXyYzZ
  • Arasında ave z(içinde [a-z]) hariç, TÜM büyük harflerdir Z.
  • Arasında Ave Z(içinde [A-Z]) hariç, TÜM küçük harflerdir a.

Görmek:

     aAbBcC[...]xXyYzZ
     |              |
from a      to      z

     aAbBcC[...]xXyYzZ
      |              |
from  A     to       Z

LC_COLLATEDeğişkeni değiştirirseniz Cbeklendiği gibi görünür:

$ export LC_COLLATE=C
$ echo [a-z]
a b c x y z
$ echo [A-Z]
A B C X Y Z

Yani, bu bir hata değil , bir harmanlama sorunu .


Aralık ifadeleri yerine veya gibi POSIX tanımlı karakter sınıflarını kullanabilirsiniz . Farklı konfigürasyonlarda ve hatta aksanlı karakterlerle de çalışırlar :upperlowerLC_COLLATE

$ echo [[:lower:]]
a b c x y z à è é
$ echo [[:upper:]]
A B C X Y Z

Bu davranış LC_ * ortam değişkenleri tarafından kontrol edilebilirse sormadım. POSIX standart komitesinde çalışıyorum ve örneğin tr, ilk önce kontrol ettiğim şeyle ilgili sorunları harmanlamayı biliyorum .
schily

@schily Sorununuzu ne eski bir bash-3, ne de bash-4 ile yeniden oluşturamam; Her ikisi de kontrol edilebilir, LC_COLLATEbu da kılavuzda belgelenmiştir.
kaos

Üzgünüm, inandıklarınızı çoğaltamıyorum, ancak kendi cevabımı görün ... Bu tartışmadaki fikirlerden sorunun nedenini keşfettim.
schily

25

[A-Z]basheşleştirmede, önce Dszsıralayan Ave sıralanan tüm harmanlama öğeleri (karakterler, ancak çağrılar aynı zamanda Macar yerellerindeki gibi karakter dizileri de olabilir ) eşleşir Z. Yerel bölgenizde cmuhtemelen B ve C arasında sıralanır.

$ printf '%s\n' A a á b B c C Ç z Z  | sort
a
A
á
b
B
c
C
Ç
z
Z

Yani cya zeşleştirileceğini olurdu [A-Z], ancak ya a.

$ printf '%s\n' A a á b B c C Ç z Z  |
pipe>  bash -c 'while IFS= read -r x; do case $x in [A-Z]) echo "$x"; esac; done'
A
á
b
B
c
C
Ç
z
Z

C yerelinde, sıra şöyle olacaktır:

$ printf '%s\n' A a á b B c C Ç z Z  | LC_COLLATE=C sort
A
B
C
Z
a
b
c
z
Ç
á

Yani [A-Z]eşleşir A, B, C, Z, ancak Çhala değil .

Büyük harflerle (herhangi bir komut dosyasında) eşleştirmek istiyorsanız, [[:upper:]]bunun yerine kullanabilirsiniz . Latin alfabesindeki bashbüyük harflerle eşleşmenin hiçbir yolu yoktur (ayrı ayrı listelenmesi dışında).

Eğer eşleşme istiyorsanız Aiçin Z İngilizce aksan olmadan mektupları, sen birini kullanabilirsiniz [A-Z]veya [[:upper:]]ancak Cyerel ayar (veri varsayarak birden fazla karakter kodlama vardır BIG5 veya GB18030 gibi karakter setleri kodlanmazsa içeren veya listeyi bu harflerin kodlamasını) ayrı ayrı onları ( [ABCDEFGHIJKLMNOPQRSTUVWXYZ]).

Mermiler arasında bazı değişiklikler olduğunu unutmayın.

İçin zsh, bash -O globasciiranges(garip isimli bash-4.3 tanıtılan opsiyon), schily-shve yash, [A-Z]kod noktası arasındadır karakterler maçları bunun Ave bu Znedenle davranışını eşdeğer olacaktır bashC yereli.

Kül, mksh ve eski kabukları için, zshyukarıdakiyle aynıdır, ancak tek baytlık karakter kümeleriyle sınırlıdır. Bu, örneğin bir UTF-8 yerel ayarında [É-Ź]eşleşmeyecek Ó, ancak bu, [<c3><89>-<c5><b9>]0x89 - 0xc5 bayt değerleriyle eşleşecek!

ksh93bashHer ikisi de küçük harflerle veya büyük harflerle başlayan özel durum aralıkları gibi davranması dışında davranır. Bu durumda, yalnızca bu uçlar arasında sıralama yapan harmanlama öğeleriyle eşleşir, ancak bunlar (veya çok karakterli harmanlama öğeleri için ilk karakterleri) aynı zamanda küçük harflerdir (veya sırasıyla büyük harflerdir). Yani [A-Z]orada eşleşir É, ancak üzerinde eolarak earalarında sıralama yapar Ave Zancak gibi büyük harfe değildir Ave Z.

For fnmatch()desenleri (olduğu gibi find -name '[A-Z]') ya da sistem normal ifadeler (olduğu gibi grep '[A-Z]'), bu sistem ve yerel ayara bağlıdır. Örneğin, burada bir GNU sistemi üzerinde, [A-Z]üzerinde uymuyor xiçinde en_GB.UTF-8yerel ayar, ancak yapar th_TH.UTF-8one. Bunu belirlemek için hangi bilgiyi kullandığı belli değil, ama görünüşe göre LC_COLLATE yerel verilerinden türetilmiş bir arama tablosuna dayanıyor ).

Tüm davranışlara POSIX tarafından izin verilir, çünkü POSIX, C yerel ayarından başka yerel ayarlarda belirtilmeyen aralıkların davranışını bırakır. Şimdi her bir yaklaşımın faydalarını tartışabiliriz.

bashbireyin yaklaşımı olduğu gibi mantıklı bir çok yapar [C-G]biz aradaki karakterleri istiyorum Cve G. Ve aralarındakileri belirleyenler için kullanıcının sıralama düzenini kullanmak en mantıklı yaklaşımdır.

Şimdi, sorun şu ki, birçok insanın, özellikle de Unicode öncesi geleneksel davranışlarda kullanılan insanlar, hatta uluslararasılaşma öncesi günlerin beklentilerini ihlal ediyor. Normal bir kullanıcıdan, bu mayıs mantıklı olsa da [C-I]içermektedir holarak hmektup arasındadır Cve Io [A-g]içermez Z, bu insanlar sadece on yıllardır ASCII icabına bakan için farklı bir konu.

Bu bashdavranış gelen de farklıdır [A-Z](olduğu gibi GNU normal ifadelerde gibi diğer GNU araçları aralığı eşleştirme grep/ sed...) veya fnmatch()olduğu gibi find -name.

Ayrıca, neyin [A-Z]eşleştiğinin çevre, işletim sistemi ve işletim sistemi sürümüne göre değiştiği anlamına gelir . Aslında [A-Z]Á maçları değil z da suboptimaldir.

zsh/ İçin yashfarklı bir sıralama düzeni kullanıyoruz. Kullanıcının karakter sırası kavramına güvenmek yerine, karakter noktası kod değerlerini kullanırız. Bu, anlaşılması kolay olmanın avantajına sahiptir, ancak ASCII dışında, pratik bir kaç noktadan itibaren, pek kullanışlı değildir. [A-Z]ABD-İngilizcesi 26 büyük harf ile, [0-9]ondalık basamaklarla eşleşir. Unicode'da bazı alfabelerin sırasını izleyen kod noktaları vardır, ancak bunlar genelleştirilmemiştir ve aynı şekilde komut dosyası kullanan farklı insanlar mutlaka harflerin sırasını kabul etmemektedir.

Geleneksel kabukları ve mksh, çizgi için, kırıldı (şimdi çoğu insan çok baytlı karakterler kullanıyor), ama öncelikle çok baytlık desteği olmadığı için. Gibi bashve kabukları için çok baytlık destek eklemek zshbüyük bir çaba ve hala devam ediyor. yash(bir Japon kabuğu), başlangıçta çok baytlık destekle tasarlanmıştı.

ksh93'ün yaklaşımı, sistemin normal ifadeleri veya fnmatch () ile tutarlı olma avantajına sahiptir (veya en azından GNU sistemlerinde görünmektedir). Orada, bazılarının [A-Z]küçük harfleri içermediği için beklentilerini bozmaz, [A-Z]içerir É(ve Á, ama Ź). Genel sortolarak strcoll()sipariş ile tutarlı değil .


1
Haklıysanız, bu LC_ * değişkenleriyle kontrol edilebilir. Farklı bir neden var gibi görünüyor.
schily

1
@cuonglm, daha çok mksh(her ikisi de pdksh'den türetilmiş). posh -c $'case Ó in [É-Ź]) echo yes; esac'hiçbir şey döndürmez.
Stéphane Chazelas

2
@schily, sortçünkü bashglobs karakter sıralama düzenine dayandığından bahsediyorum . Şu anda böyle eski bir sürüme erişimim yok bash, ancak daha sonra kontrol edebilirim. O zaman farklı mıydı?
Stéphane Chazelas

1
Tekrar söyleyeyim: zsh, POSIX-ksh88, ksh93t + Bourne Shell, hepsi beklediğim gibi davranıyor. Bash farklı davranan tek kabuktur ve bu durumda yerel bash ile bash kontrol edilemez.
schily

2
@schily, bu not \xFFvardır bayt 0XFF değil, karakter U +, 00FF ( ÿkendisi 0xC3 0xBF olarak kodlanmış). \xFFtek başına geçerli bir karakter oluşturmuyor, bu yüzden neden eşleştirilmesi gerektiğini göremiyorum [É-Ź].
Stéphane Chazelas

9

Bu, bashdokümantasyon, desen eşleştirme bölümünde belgelenmiştir ve belgelenmiştir . Range ifadesi , geçerli yerel ayarın harmanlama sırasını ve karakter kümesini [X-Y]arasındaki Xve Ykullanan karakterleri içerecektir :

LC_ALL=en_US.utf8 bash -c 'case b in [A-Z]) echo yes; esac' 
yes

Yerel barasında Ave Zarasında sıralanmış görebilirsiniz en_US.utf8.

Bu davranışı önlemek için bazı seçenekleriniz var:

# Setting LC_ALL or LC_COLLATE to C
LC_ALL=C bash -c 'echo [A-Z]*'

# Or using POSIX character class
LC_ALL=C bash -c 'echo [[:upper:]]*'

veya etkinleştir globasciiranges(bash 4.3 ve üstü ile):

bash -O globasciiranges -c 'echo [A-Z]*'

6

Bu davranışı yeni bir Amazon EC2 örneğinde gözlemledim. OP bir MCVE önermediğinden , bir tane göndereceğim :

$ cd $(mktemp -d)
$ touch foo
$ echo [A-Z]*     # prepare for a surprise!
foo

$ echo $BASH_VERSION
4.1.2(1)-release
$ uname -a
Linux spinup-tmp12 3.14.27-25.47.amzn1.x86_64 #1 SMP Wed Dec 17 18:36:15 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$ env | grep LC_  # no locale, let's set one
$ LC_ALL=C
$ echo [A-Z]*
[A-Z]*

$ unset LC_ALL    # ok, good. what if we go back to no locale?
$ echo [A-Z]*
foo

Bu yüzden, LC_*setimin olmaması bash 4.1.2 (1) 'i açık bir şekilde garip davranışlar üretmek için Linux'ta yeniden yayınladı. İlgili yerel ayar değişkenlerini ayarlayıp ayarlayarak tuhaf davranışı güvenilir şekilde değiştirebilirim. Şaşırtıcı olmayan bir şekilde, bu davranış dışa aktarma yoluyla tutarlı görünüyor:

$ export LC_ALL=C
$ bash
$ echo [A-Z]*
[A-Z]*
$ exit
$ echo $SHLVL
1
$ unset LC_ALL
$ bash
$ echo [A-Z]*
foo

Bash'ın Stéphane "Shellshock" Chazelas'ın cevap verdiği gibi davrandığını görmeme rağmen , kalıp eşleştirme ile ilgili bash belgelerinin buggy olduğunu düşünüyorum :

Örneğin , varsayılan C yerel ayarında , '[a-dx-z]', '[abcdxyz]' değerine eşittir.

Cümlenin (benimkine işaret eden) "ilgili yerel ayar değişkenleri ayarlanmamışsa bash varsayılan C yerel ayarına gidecektir" olarak okudum. Bash bunu yapıyor gibi görünmüyor. Bunun yerine, karakterlerin diakritik katlama ile sözlük sıralamasına göre sıralandığı bir yerel ayar için varsayılan olarak görünüyor:

$ echo [A-E]*
[A-E]*
$ echo [A-F]*
foo
$ touch "évocateur"
$ echo [A-F]*
foo évocateur

Bence bash için LC_*(özellikle LC_CTYPEve LC_COLLATE) tanımsız olduğunda nasıl davranacağını belgelemek iyi olur . Ama bu arada, ben biraz bilgelik paylaşacağım :

... [karakter aralıkları] ile çok dikkatli olmalısınız çünkü doğru bir şekilde yapılandırılmadıkça beklenen sonuçları vermeyeceklerdir. Şimdilik, bunları kullanmaktan kaçınmalı ve bunun yerine karakter sınıflarını kullanmalısınız.

ve

Gerçekten uygunsanız ve / veya çok yerel bir ortam için komut dosyası kullanıyorsanız, dosyaları eşleştirirken yerel ayar değişkenlerinizin ne olduğunu bildiğinizden veya bir kodda kodladığınızdan emin olmak en iyi yöntemdir. tamamen genel yol.


Güncelleme @ G-Man yorumlarına dayanarak, neler olduğunu daha yakından inceleyelim:

$ env | grep LANG
LANG=en_US.UTF-8

Ah, ha! Bu daha önce görülen harmanlamayı açıklar. Tüm yerel değişkenleri kaldıralım:

$ unset LANG LANGUAGE LC_ALL
$ env | grep 'LC_|LANG'
$ echo [A-Z]*
[A-Z]*

Oraya gidiyoruz. Şimdi bash, bu Linux sistemindeki dokümantasyon konusunda tutarlı bir şekilde çalışmaktadır. Yerel değişken herhangi ayarlanırsa ( LANGUAGE, LANG, LC_COLLATE, LC_CTYPE, LC_ALL, vs.) ve Bash onun el ile uygun olanlar kullanılır. Aksi takdirde, bash C'ye geri döner.

Wooledge bash SSS şunu söylemek etti:

Son GNU sistemlerinde, değişkenler bu sırada kullanılır. LANGUAGE ayarlanmışsa, LANG C olarak ayarlanmadıkça kullanın, bu durumda LANGUAGE göz ardı edilir. Ayrıca, bazı programlar sadece DİLİ kullanmazlar. Aksi takdirde, LC_ALL ayarlanmışsa, bunu kullanın. Aksi takdirde, bu kullanımı kapsayan belirli LC_ * değişkeni ayarlanmışsa, bunu kullanın. (Örneğin, LC_MESSAGES hata mesajlarını kapsar.) Aksi takdirde, LANG kullanın.

Bu nedenle, hem operasyon hem de dokümantasyondaki görünen sorun, tüm yerel sürüş değişkenlerinin toplamına bakarak açıklanabilir.


Hiçbir LC_variable mevcut değilse ve bash, Cyerel ayar için belgelenen şekilde davranmazsa , bu bir hatadır.
schily

1
@bishop: (1) Yazım hatası: MVCE MCVE olmalıdır. (2) Örneğinizin tamamlanmasını istiyorsanız, eklemelisiniz env | grep LANGveya echo "$LANG".
G-Man 'Monica'yı Yeniden Girin' Dedi

@schily Daha fazla araştırma beni bu Linux sistemindeki belgelerde veya işlemlerde hiçbir hata olmadığı konusunda ikna etti.
piskopos

@ G-Man Teşekkürler! Ben unuttum LANG. Bu ipucu ile her şey açıklanmıştır.
piskopos

LANG, 1988'de Sun tarafından ilk yerelleştirme girişimleri için, tek bir değişkenin yeterli olmadığını keşfetmeden önce tanıtıldı. Bugün bir geri dönüş olarak kullanılır ve LC_ALL zorunlu üzerine yazma olarak kullanılır.
schily

3

Yerel ayar, hangi karakterlerin eşleşeceğini değiştirebilir [A-Z]. kullanım

(LC_ALL=C; rm [A-Z]*)

etkisini ortadan kaldırmak için. (Değişikliği yerelleştirmek için alt kabuk kullandım).


Bu işe yaramaz, hala tüm harflerle eşleşir
schily

7
Bu işe yaramaz çünkü glob rm çalıştırılmadan önce yapılmıştır. export LC_ALL=Cİlk önce dene .
cuonglm

Üzgünüz, bash ile ilgili olan soruyu yanlış anlıyorsunuz ve rm ile değil.
schily

@schily: Evet, yanılmışım, ifadeleri ayırmanız gerekiyor. Güncellemeyi kontrol et.
choroba

2

Daha önce de belirtildiği gibi, bu bir "harmanlama emri" sorunudur.

Az, bazı yerlerde büyük harfler içerebilir:

     aAbBcC[...]xXyYzZ
     |              |
from a      to      z

Bash 4.3'ten beri doğru çözüm seçeneği ayarlamaktır globasciiranges:

shopt -s globasciiranges

bash'ın globlama aralıklarında LC_COLLATE=Cayarlanmış gibi davranmasını sağlamak .


-6

Sanırım kendi soruma doğru cevabı buldum:

Bash kendi yerel ayarını yönetmediği için adamcağız. Dolayısıyla bir bash işleminde LC_ * ayarının bu kabuk işleminde etkisi yoktur.

LC_COLLATE = C ayarladıysanız ve başka bir bash başlattıysanız, globbing yeni bash işleminde beklendiği gibi çalışır.


2
Hiçbir yerimde değil.
kaos

2
Bunu, makinemdeki bash sürümlerinde onaylamıyorum, exportdüzgün yapmamışsınız gibi geliyor .
Chris Down

Yani, düzgün bir şekilde ihraç edilen bir şeyin, yeni bir bash işlemini etkileyecek şekilde düzgün bir şekilde ihraç edilmediğine inanıyorsunuz?
schily

4
Solaris'in çevreyi ele alması kesin bir şekilde eksiktir, bu nedenle, bashtaki "böcek", Solaris'e özgü bir geçici çözüm bulunmadığı takdirde şaşırmam.
Ocaklar

1
@schily: Kabuk içindeki LC_ * değişkenlerini değiştirmenin kendi yerel durumunu güncellemesine neden olduğu konusunda bir alıntı yaptınız mı? Ben tam tersini düşünürdüm. Özellikle bir betiği çalıştıran bir kabuk için, komut dosyasının bir metin dosyası olması ve "metin dosyası" yalnızca bir bağlam içinde anlamlı olduğundan, komut dosyasının ayrıştırılması / yürütülmesi yoluyla orta yolun değiştirilmesi iyi tanımlanmış bir davranışa sahip olmaz. tek karakter kodlaması.
R. ..
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.