Czy symbol wieloznaczny Bash star * zawsze tworzy posortowaną (rosnącą) listę?

53

Mam katalog wypełniony plikami o nazwach takich jak logXXgdzie XX to dwuznakowa, zerowa, duża liczba szesnastkowa, na przykład:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

Ogólnie będzie mniej niż powiedzmy 20 lub 30 plików ogółem. Data i godzina w moim konkretnym systemie nie są czymś, na czym można polegać (system osadzony bez wiarygodnych źródeł czasu NTP lub GPS). Jednak nazwy plików będą niezawodnie zwiększać, jak pokazano powyżej.

Chciałbym grepprzejrzeć wszystkie pliki dla jednego najnowszego wpisu dziennika określonego typu, miałem nadzieję catna pliki razem, takie jak ...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Jednak dotarło do mnie, że różne wersje bashlub shlub zshitd. Mogą mieć różne wyobrażenia o tym, jak *jest rozwinięty.

man bashStrona nie powiedzieć, czy ekspansja *byłaby zdecydowanie rosnąco alfabetyczna lista pasujących nazw. Wygląda na to, że rośnie w górę za każdym razem, gdy wypróbowałem go na wszystkich dostępnych mi systemach - ale czy jest to ZDEFINIOWANE zachowanie, czy tylko specyficzne dla implementacji?

Innymi słowy, czy mogę całkowicie polegać na cat /tmp/logs/log*konkatenacji wszystkich moich plików dziennika w kolejności alfabetycznej?

Wossname
źródło
1
@ ADDB Domyślna kolejność sortowania sortjest taka sama jak w przypadku powłoki, gdy rozwija ona wzorzec globowania nazw plików.
Kusalananda
9
To okropna praktyka nazywania plików. Dlaczego zaczynasz swój bieg od log (0) = - infty?
PE
14
@EP Nasz system plików to złożony 7-wymiarowy hipertoroid z surrealistyczną numeracją i-węzłów. Został dziadek z jakąś niejasną gałęzią busybox i utknęliśmy z tym teraz :)
Wossname
1
Możesz ominąć za catpomocą, grep -h pattern /tmp/logs/log*aby ukryć poprzedzające nazwy plików do dopasowań. (Przynajmniej z GNU grep, nie sprawdziłem POSIX-a lub busyboksa.)
Peter Cordes
1
@Kusalananda Słyszeliście o bezużytecznym użyciu cat, jest to bezużyteczne użyciesort
cat

Odpowiedzi:

52

We wszystkich powłokach globusy są domyślnie sortowane. Byli już przy pomocy /etc/globpomocnika zwanego przez powłokę Kena Thompsona, aby rozwinąć globusy w pierwszej wersji Uniksa na początku lat 70. (i które dały globusom swoją nazwę).

Ponieważ shPOSIX wymaga sortowania według sposobu strcoll(), tzn. Przy użyciu kolejności sortowania w ustawieniach regionalnych użytkownika, tak jak lschoć niektórzy nadal to robią strcmp(), opartej tylko na wartościach bajtów.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Możesz zauważyć powyżej, że w przypadku powłok wykonujących sortowanie na podstawie ustawień regionalnych, tutaj w systemie GNU z en_GB.UTF-8ustawieniami narodowymi, -nazwy plików są ignorowane podczas sortowania (większość znaków interpunkcyjnych). Jest óon sortowany w bardziej oczekiwany sposób (przynajmniej dla Brytyjczyków), a wielkość liter jest ignorowana (z wyjątkiem przypadków decydujących o powiązaniach).

Jednak zauważysz pewne niespójności dla log for log②. Jest tak, ponieważ kolejność sortowania ① i ② nie jest zdefiniowana w ustawieniach narodowych GNU (obecnie; mam nadzieję, że zostanie to kiedyś naprawione). Sortują to samo, więc otrzymujesz losowe wyniki.

Zmiana ustawień regionalnych wpłynie na kolejność sortowania. Możesz ustawić ustawienia regionalne na C, aby uzyskać strcmp()sortowanie podobne do:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

Zauważ, że niektóre ustawienia narodowe mogą powodować pewne nieporozumienia, nawet w przypadku ciągów znaków all-ASCII all-alnum. Jak te czeskich (w systemach GNU przynajmniej) gdzie chjest to elementem porównawczym , który sortuje po h:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

Lub, jak wskazał @ninjalj, jeszcze dziwniejsze w węgierskich lokalizacjach:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

W zshmożesz wybrać sortowanie za pomocą kwalifikatorów glob . Na przykład:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

Numeryczne sortowanie echo *(n)można również włączyć globalnie za pomocą numericglobsortopcji:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Jeśli jesteś (tak jak ja) zdezorientowany tym zamówieniem w tym konkretnym przypadku (tutaj, używając mojej brytyjskiej lokalizacji), zobacz tutaj, aby uzyskać szczegółowe informacje.

Stéphane Chazelas
źródło
1
Przypadek „ch” może być jeszcze dziwniejszy: niektóre lokalizacje mogą zdecydować, że „ch”, „Ch” i „CH” są po 1 elemencie zestawiającym, a „cH” to dwa elementy zestawiające. Zobacz: unicode.org/cldr/trac/ticket/889 Obecny CLDR nie wydaje się być całkowicie spójny: obecny węgierski ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) ma takie zasady &C<cs<<<Cs<<<CS, natomiast &C<cs<<<cS<<<Cs<<<CSjest oznaczony jako proponowany projekt eksperymentalny. Sądząc z niektórych starszych danych zaimportowanych do CLDR, starsze systemy AIX i MS wydawały się preferować widok „małe litery, a potem wielkie litery to 2 różne elementy sortowania”.
ninjalj
I widziałem systemy, w których i tak to nie działało. :(
Joshua
38

Strona podręcznika bash określa:

Rozszerzenie nazwy ścieżki

Po podziałowi na słowa, chyba że -fopcja została ustawiona, bash skanuje każde słowo znakami *, ?oraz [. Jeśli pojawi się jeden z tych znaków, to słowo jest traktowane jako wzór i zastępowane alfabetycznie uporządkowaną listą nazw plików pasujących do wzorca […].

użytkownik4556274
źródło
1
Właśnie znalazłem interesujący błąd w manrenderowaniu szpachli lub tekstu ... jeśli szukany tekst zostanie „zawinięty”, wówczas polecenie / search go nie znajdzie. Właśnie zmaksymalizowałem mój terminal i
oto
2
Objąłeś bash. Tho OP był również zainteresowany „zsh itp.”
Kusalananda
29

O ile nie uruchomisz niektórych bardzo konkretnych opcji powłoki w niektórych powłokach, wynik będzie na pewno taki sam.

Kolejność jest określona w standardzie POSIX :

Jeśli wzorzec pasuje do istniejących nazw plików lub ścieżek, wzór należy zastąpić tymi nazwami plików i ścieżkami, posortowanymi zgodnie z kolejnością zestawiania obowiązującą w bieżących ustawieniach narodowych . Jeśli ta sekwencja zestawiania nie ma całkowitego uporządkowania wszystkich znaków (patrz XBD LC_COLLATE), wszelkie nazwy plików lub ścieżki, które są zestawiane równo, powinny być dalej porównywane bajt po bajcie przy użyciu sekwencji zestawiania dla ustawień narodowych POSIX.

Zobacz także LC_COLLATE Kategoria w Locale POSIX , która w skrócie mówi, że jeśli LC_COLLATE=C, to rzeczy są uporządkowane w kolejności ASCII.


bashInstrukcja wspomina

LC_COLLATE

Ta zmienna określa kolejność zestawiania używaną podczas sortowania wyników interpretacji nazw ścieżek, a także określa zachowanie wyrażeń zakresowych, klas równoważności i sekwencji zestawiania w ramach interpretacji nazw ścieżek i dopasowywania wzorców.

ksh93i zshma podobne sformułowanie, co prowadzi mnie do przekonania, że ​​pod tym względem są zgodne ze standardem POSIX.

Inne powłoki, takie jak pdkshi dashnie mówią nic o sortowaniu nazw plików wynikających z globowania nazw plików. Kusi mnie, aby wierzyć, że oznacza to, że nadal przestrzegają tego samego standardu, przynajmniej podczas korzystania z ustawień regionalnych POSIX. Z mojego doświadczenia nie spotkałem powłoki, która jawnie „dziwnie” sortuje nazwy plików ASCII.

Kusalananda
źródło
2
Zobacz numericglobsortopcję zsh, która wpłynie na sortowanie. Chociaż wolę włączyć tę opcję dla poszczególnych globów, echo *(n)niż włączać tę opcję globalnie.
Stéphane Chazelas
Nitpick. Bash w trybie domyślnym NIE jest zgodny z Posix.
fpmurphy
@ fpmurphy1 Powiedz więcej.
Kusalananda
@Kusalananda. Bash nigdy nie został certyfikowany jako skarga POSIX. Aby uzyskać „zgodność z POSIX” w Bash, musisz wywołać Bash z --posixopcją wiersza poleceń lub wykonaćset -o posix
fpmurphy
@ fpmurphy1 Tak, ale posixtryb Basha nie wpływa na sortowanie rozszerzenia znaków globowania nazw plików . Zobacz gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html To prowadzi mnie do przekonania (raczej raczej), że sortowanie jest zgodne z POSIX.
Kusalananda
1

Jeśli głównym celem jest sortowanie plików wejściowych według wieku, najpierw najstarsze, możesz napisać

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

A jeśli w grę wchodzą również dzienniki obrócone i skompresowane:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
sultansofswing
źródło
4
Wspomniano, że nie można ufać znacznikom czasowym plików.
Kusalananda
3
@Kusalananda, zgadza się, nasz czas systemowy jest generalnie uważany za generator liczb losowych :)
Wossname