We wszystkich znanych mi powłokach rm [A-Z]*
usuwa wszystkie pliki rozpoczynające się na wielką literę, ale w przypadku bash powoduje to usunięcie wszystkich plików rozpoczynających się na literę.
Ponieważ ten problem występuje w systemach Linux i Solaris w wersjach bash-3 i bash-4, nie może to być błąd spowodowany błędnym dopasowaniem wzorca w libc lub błędnie skonfigurowaną definicją ustawień regionalnych.
Czy to dziwne i ryzykowne zachowanie jest zamierzone, czy to tylko błąd, który istnieje od wielu lat?
locale
wynik? Nie mogę tego odtworzyć (touch foo; echo [A-Z]*
wypisuje dosłowny wzorzec, a nie „foo”, w innym pustym katalogu).# echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*
A b B z ZABZOdpowiedzi:
LC_COLLATE
jest zmienną, która określa kolejność sortowania używaną podczas sortowania wyników interpretacji nazw ścieżek oraz determinuje zachowanie wyrażeń zakresowych, klas równoważności i sekwencji zestawiania w ramach interpretacji nazw ścieżek i dopasowywania wzorców.Rozważ następujące:
Zwróć uwagę, że po
echo [a-z]
wywołaniu polecenia oczekiwanymi danymi wyjściowymi byłyby wszystkie pliki zawierające małe litery. Ponadto przyecho [A-Z]
plikach zawierających wielkie litery można się spodziewać.Standardowe zestawienia z ustawieniami regionalnymi, takie jak,
en_US
mają następującą kolejność:a
iz
(w[a-z]
) są WSZYSTKIE wielkie litery, z wyjątkiemZ
.A
iZ
(w[A-Z]
) są WSZYSTKIE małe litery, z wyjątkiema
.Widzieć:
Jeśli zmienisz
LC_COLLATE
zmienną,C
będzie wyglądać zgodnie z oczekiwaniami:Więc to nie jest błąd , to problem z sortowaniem .
Zamiast wyrażeń zakresu możesz użyć klas znaków zdefiniowanych w POSIX , takich jak
upper
lublower
. Działają również w różnychLC_COLLATE
konfiguracjach, a nawet z akcentowanymi postaciami :źródło
tr
więc sprawdziłem to najpierw.LC_COLLATE
co jest również udokumentowane w instrukcji.[A-Z]
wbash
dopasowuje wszystkie elementy zestawiające (znaki, ale wywołanie może być również ciągiem znaków jakDsz
w węgierskich ustawieniach regionalnych), które sortują po,A
a sortują przedZ
. W twoim regioniec
prawdopodobnie sortuje się pomiędzy B i C.Tak
c
lubz
byłoby pasować[A-Z]
, ale nieẐ
luba
.W ustawieniach regionalnych C kolejność byłaby następująca:
Więc
[A-Z]
będzie pasowaćA
,B
,C
,Z
, ale nieÇ
i nadal nieẐ
.Jeśli chcesz dopasować wielkie litery (w dowolnym skrypcie), możesz użyć
[[:upper:]]
zamiast tego. Nie ma wbudowanego sposobu,bash
aby dopasowywać tylko wielkie litery w skrypcie łacińskim (z wyjątkiem listowania ich indywidualnie).Jeśli chcesz, aby dopasować
A
się doZ
angielskich liter bez znaków diakrytycznych, można użyć[A-Z]
albo[[:upper:]]
ale wC
regionie (przy założeniu, że dane nie są kodowane w zestawach znaków, takich jak BIG5 lub GB18030 który ma kilka znaków, których kodowanie zawiera kodowanie tych liter) lub listy je indywidualnie ([ABCDEFGHIJKLMNOPQRSTUVWXYZ]
).Zauważ, że istnieją pewne różnice między powłokami.
For
zsh
,bash -O globasciiranges
(dziwnie nazwana opcja wprowadzona w bash-4.3)schily-sh
iyash
,[A-Z]
pasuje do znaków, których punkt kodowy znajduje się między tymA
a tymZ
, więc byłoby to równoważne zachowaniubash
w ustawieniach regionalnych C.Dla popiołu, mksza i starożytnych pocisków, takich samych jak
zsh
powyżej, ale ograniczonych do jednobajtowych zestawów znaków. Oznacza to, że na przykład w ustawieniach regionalnych UTF-8[É-Ź]
nie pasowałobyÓ
, ale ponieważ tak[<c3><89>-<c5><b9>]
, pasowałyby do wartości bajtów 0x89 do 0xc5!ksh93
zachowuje się takbash
, ale traktuje jako zakresy przypadków specjalnych, których końce zaczynają się małymi lub dużymi literami. W takim przypadku dopasowuje się tylko w elementach zestawiających, które sortują między tymi końcami, ale które (lub ich pierwszy znak w przypadku elementów zestawiających wiele znaków) są również pisane małymi literami (lub odpowiednio dużymi literami). Więc[A-Z]
nie będzie pasowaćÉ
, ale nie nae
jake
robi porządek międzyA
aZ
, ale nie jest wielka, jakA
iZ
.W przypadku
fnmatch()
wzorców (jak wfind -name '[A-Z]'
) lub systemowych wyrażeń regularnych (jak wgrep '[A-Z]'
) zależy to od systemu i ustawień regionalnych. Na przykład w systemie GNU tutaj[A-Z]
nie pasujex
wen_GB.UTF-8
ustawieniach regionalnych, ale w tymth_TH.UTF-8
jednym. Nie jest dla mnie jasne, jakich informacji używa, aby to ustalić, ale najwyraźniej opiera się na tabeli odnośników pochodzącej z danych regionalnych LC_COLLATE ).Wszystkie zachowania są dozwolone przez POSIX, ponieważ POSIX pozostawia zachowanie zakresów nieokreślonych w ustawieniach regionalnych innych niż ustawienia regionalne C. Teraz możemy spierać się o zalety każdego podejścia.
bash
Podejście to ma wiele sensu[C-G]
, ponieważ chcemy, aby postacie były pomiędzyC
aG
. I stosując porządek użytkownika za to, co określa, co w międzyczasie jest najbardziej logicznym rozwiązaniem.Problem polega na tym, że przełamuje oczekiwania wielu ludzi, zwłaszcza tych, którzy przywykli do tradycyjnego zachowania przed Unicode, nawet dni poprzedzających internacjonalizację. Choć od normalnego użytkownika, to sprawia, maja poczucie, że
[C-I]
zawierah
jakoh
list jest międzyC
aI
i że[A-g]
nie obejmujeZ
, to inna sprawa dla osób mających do czynienia z ASCII tylko przez dziesięciolecia.To
bash
zachowanie różni się również od[A-Z]
dopasowania zakresu w innych narzędziach GNU, takich jak wyrażenia regularne GNU (jak wgrep
/sed
...) lubfnmatch()
jak wfind -name
.Oznacza to również, że to, co
[A-Z]
pasuje, różni się w zależności od środowiska, systemu operacyjnego i wersji systemu operacyjnego. Fakt, że[A-Z]
pasuje do Á, ale nie Ź, jest również nieoptymalny.Dla
zsh
/yash
używamy innego porządku sortowania. Zamiast polegać na pojęciu kolejności znaków przez użytkownika, używamy wartości kodu punktu znakowego. Ma to tę zaletę, że jest łatwe do zrozumienia, ale z praktycznego punktu widzenia niewielu, poza ASCII, nie jest bardzo przydatne.[A-Z]
dopasowuje 26 wielkich amerykańskich liter w języku amerykańskim,[0-9]
dopasowuje cyfry dziesiętne. Istnieją punkty kodowe w Unicode, które są zgodne z kolejnością niektórych alfabetów, ale nie są uogólnione i nie mogą być uogólnione, ponieważ w każdym razie różni ludzie używający tego samego skryptu niekoniecznie zgadzają się na kolejność liter.W przypadku tradycyjnych powłok i mksh, myślnik jest zepsuty (teraz, gdy większość ludzi używa znaków wielobajtowych), ale przede wszystkim dlatego, że nie ma jeszcze obsługi wielu bajtów. Dodanie obsługi wielu bajtów do powłok takich jak
bash
izsh
było dużym wysiłkiem i wciąż trwa.yash
(japońska powłoka) od samego początku była projektowana z obsługą wielu bajtów.Podejście ksh93 ma tę zaletę, że jest spójne z wyrażeniami regularnymi systemu lub fnmatch () (lub przynajmniej wydaje się, że przynajmniej w systemach GNU). Nie łamie to oczekiwań niektórych osób, ponieważ
[A-Z]
nie zawiera małych liter,[A-Z]
obejmujeÉ
(i Á, ale nie Ź). To nie jest zgodne zsort
lub ogólniestrcoll()
zamówienie.źródło
mksh
(oba pochodzą z pdksh).posh -c $'case Ó in [É-Ź]) echo yes; esac'
nic nie zwraca.sort
ponieważbash
globusy są oparte na kolejności sortowania znaków. Obecnie nie mam dostępu do tak starej wersjibash
, ale mogę to sprawdzić później. Czy wtedy było inaczej?\xFF
istnieje bajt 0xFF, a nie znak U + 00FF (ÿ
sam kodowany jako 0xC3 0xBF).\xFF
sam nie tworzy prawidłowej postaci, więc nie rozumiem, dlaczego powinna być dopasowana[É-Ź]
.Jest przeznaczony i udokumentowany w
bash
dokumentacji, sekcji dopasowania wzorców . Wyrażenie zakresu[X-Y]
będzie zawierać dowolne znaki pomiędzyX
iY
przy użyciu kolejności zestawiania i zestawu znaków w bieżącym locale:Widać,
b
klasyfikowane międzyA
orazZ
wen_US.utf8
lokalizacji.Masz kilka możliwości, aby temu zapobiec:
lub włącz
globasciiranges
(w wersji bash 4.3 i nowszej):źródło
Zauważyłem to zachowanie w nowej instancji Amazon EC2. Ponieważ OP nie zaoferował MCVE , opublikuję jeden:
Więc nie mając mojego
LC_*
zestawu prowadzi bash 4.1.2 (1) -release w Linuksie, aby wywołać pozornie dziwne zachowanie. Mogę niezawodnie przełączać nieparzyste zachowanie, ustawiając i odznaczając odpowiednie zmienne regionalne. Nic dziwnego, że zachowanie to wydaje się spójne podczas eksportowania:Podczas gdy widzę, że bash zachowuje się tak, jak odpowiedział Chazelas Stéphane „Shellshock” , myślę, że dokumentacja bash na temat dopasowania wzorca jest błędna:
Czytam to zdanie (moje podkreślenie) jako „jeśli odpowiednie zmienne ustawień regionalnych nie są ustawione, wówczas bash domyślnie ustawi się na ustawienia regionalne języka C”. Bash chyba tego nie robi. Zamiast tego wydaje się, że domyślnie ustawiony jest język, w którym znaki są sortowane w kolejności słownikowej ze składaniem znaków diakrytycznych:
Myślę, że dobrze byłoby, gdyby bash udokumentował, jak się zachowa, gdy
LC_*
(konkretnieLC_CTYPE
iLC_COLLATE
) nie zostanie zdefiniowany. Ale tymczasem podzielę się mądrością :i
Aktualizacja Na podstawie komentarza @ G-Man przyjrzyjmy się bliżej temu, co się dzieje:
Ach, ha! To wyjaśnia zestawienie widoczne wcześniej. Usuńmy wszystkie zmienne regionalne:
No to jedziemy. Teraz bash działa konsekwentnie w odniesieniu do dokumentacji tego systemu Linux. Jeżeli każdy ze zmiennych lokalizacji są ustawione (
LANGUAGE
,LANG
,LC_COLLATE
,LC_CTYPE
,LC_ALL
, itd.), A następnie wykorzystuje te atakujących zgodnie z instrukcją. W przeciwnym razie bash wraca do C.Wooledge bash FAQ ma do powiedzenia:
Widoczny problem, zarówno w działaniu, jak i dokumentacji, można wyjaśnić, analizując całkowitą sumę wszystkich zmiennych sterujących ustawieniami narodowymi.
źródło
C
ustawień regionalnych, oznacza to błąd.env | grep LANG
lubecho "$LANG"
.LANG
. Dzięki tej podpowiedzi wszystko zostało wyjaśnione.Ustawienia regionalne mogą zmieniać, które znaki są dopasowane
[A-Z]
. Posługiwać sięaby wyeliminować wpływ. (Użyłem podpowłoki, aby zlokalizować zmianę).
źródło
export LC_ALL=C
najpierw.Jak już powiedziano, jest to kwestia „kolejności zestawiania”.
Zakres az może zawierać duże litery w niektórych lokalizacjach:
Prawidłowym rozwiązaniem od czasu bash 4.3 jest ustawienie opcji
globasciiranges
:aby bash działał tak, jakby
LC_COLLATE=C
został ustawiony w globalnych zakresach.źródło
Wygląda na to, że znalazłem właściwą odpowiedź na moje pytanie:
Bash jest błędny, ponieważ nie zarządza własnymi ustawieniami narodowymi. Zatem ustawienie LC_ * w procesie bash nie ma wpływu na ten proces powłoki.
Jeśli ustawisz LC_COLLATE = C, a następnie uruchomisz kolejny bash, globbing działa zgodnie z oczekiwaniami w nowym procesie bash.
źródło
export
tego poprawnie.