Mám adresář plný souborů s názvy jako logXX
kde XX je dvouznakové hexadecimální číslo odložené nulou, například:
log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...
Obecně bude celkem méně než řekněme 20 nebo 30 souborů. Datum a čas v mém konkrétním systému není něco, na co se lze spolehnout (vestavěný systém bez spolehlivých zdrojů času NTP nebo GPS). Názvy souborů se však budou spolehlivě zvyšovat, jak je uvedeno výše.
Chci grep
doufal jsem, že projdu všechny soubory pro poslední záznam protokolu určitého typu cat
soubory dohromady, například…
cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1
Napadlo mě však, že různé verze bash
nebo sh
nebo zsh
atd. mohou mít různé představy o tom, jak *
je rozšířena.
man bash
stránka neříká, zda rozšíření *
či nikoli by byl rozhodně vzestupný abecední seznam odpovídajících názvů souborů. Zdá se, že stoupá pokaždé, když to zkouším na všech systémech, které mám k dispozici – ale jedná se o DEFINOVANÉ chování nebo pouze o specifickou implementaci?
Jinými slovy, mohu se absolutně spolehnout na cat /tmp/logs/log*
zřetězit všechny mé soubory protokolu dohromady v abecedním pořadí?
Přijatá odpověď:
Ve všech shellech jsou globusy standardně seřazeny. Byly již součástí /etc/glob
pomocník povolaný shellem Kena Thompsona k rozšíření globů v první verzi Unixu na počátku 70. let (a která dala globům jejich jméno).
Pro sh
, POSIX vyžaduje, aby byly seřazeny pomocí strcoll()
, což je použití pořadí řazení v národním prostředí uživatele, jako pro ls
i když někteří to stále dělají pomocí strcmp()
, který je založen pouze na hodnotách bajtů.
$ 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
Výše si můžete všimnout, že u těch shellů, které třídí na základě národního prostředí, zde na systému GNU s en_GB.UTF-8
národní prostředí, -
v názvech souborů je ignorováno pro řazení (většina interpunkčních znaků by). ó
se třídí očekávanějším způsobem (alespoň pro Brity) a velká a malá písmena se ignorují (kromě případů, kdy dojde na rozhodování o remíze).
U log① log② si však všimnete určitých nesrovnalostí. Je to proto, že pořadí řazení ① a ② není definováno v národních prostředích GNU (aktuálně; doufejme, že to jednoho dne bude opraveno). Třídí stejně, takže získáte náhodné výsledky.
Související:Zpracovat potomky?
Změna národního prostředí ovlivní pořadí řazení. Můžete nastavit národní prostředí na C a získat strcmp()
-jako řazení:
$ 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
Všimněte si, že některá národní prostředí mohou způsobit určité zmatky i pro řetězce all-ASCII all-alnum. Jako ty české (alespoň na systémech GNU), kde ch
je prvek řazení který se řadí po h
:
$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch
Nebo, jak poukázal @ninjalj, ještě podivnější v maďarských lokalitách:
$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy
V zsh
, můžete zvolit řazení pomocí glob kvalifikátorů. Například:
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.
Číselný druh echo *(n)
lze také povolit globálně pomocí numericglobsort
možnost:
$ 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
Pokud jste (stejně jako já) zmateni tímto pořadím v tomto konkrétním případě (zde pomocí mého britského národního prostředí), podrobnosti naleznete zde.