O curinga Bash star * sempre produz uma lista classificada (crescente)?

O curinga Bash star * sempre produz uma lista classificada (crescente)?

Eu tenho um diretório cheio de arquivos com nomes como logXXonde XX é um número hexadecimal maiúsculo de dois caracteres, preenchido com zeros, como:

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

Geralmente haverá menos de 20 ou 30 arquivos no total. A data e a hora em meu sistema específico não são confiáveis ​​(um sistema incorporado sem fontes de tempo NTP ou GPS confiáveis). No entanto, os nomes dos arquivos serão incrementados de forma confiável, conforme mostrado acima.

Desejo grepexaminar todos os arquivos da entrada de log mais recente de um determinado tipo. Esperava catreunir os arquivos, como ...

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

No entanto, ocorreu-me que diferentes versões de bashou shou zshetc. podem ter ideias diferentes sobre como o *é expandido.

A man bashpágina não diz se a expansão *seria ou não uma lista alfabética definitivamente ascendente de nomes de arquivos correspondentes. Parece estar aumentando toda vez que eu tentei em todos os sistemas que tenho disponíveis - mas é um comportamento DEFINIDO ou apenas específico da implementação?

Em outras palavras, posso confiar totalmente cat /tmp/logs/log*para concatenar todos os meus arquivos de log em ordem alfabética?

Responder1

Em todos os shells, os globs são classificados por padrão.Eles já estavam pelo /etc/globajudantechamado pelo shell de Ken Thompson para expandir globs na primeira versão do Unix no início dos anos 70 (e que deu nome aos globs).

Para sh, o POSIX exige que eles sejam classificados por meio de strcoll(), ou seja, usando a ordem de classificação na localidade do usuário, embora lsalguns ainda façam isso por meio de strcmp(), que é baseado apenas em valores de bytes.

$ 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

Você pode notar acima que para aqueles shells que fazem a classificação com base na localidade, aqui em um sistema GNU com uma en_GB.UTF-8localidade, o -nome dos arquivos é ignorado para classificação (a maioria dos caracteres de pontuação seria). O caso óé resolvido de uma forma mais esperada (pelo menos para os britânicos), e o caso é ignorado (exceto quando se trata de decidir empates).

No entanto, você notará algumas inconsistências no log① log②. Isso ocorre porque a ordem de classificação de ① e ② não está definida nas localidades GNU (atualmente; espero que seja corrigida algum dia). Eles classificam da mesma forma, então você obtém resultados aleatórios.

Alterar a localidade afetará a ordem de classificação. Você pode definir a localidade como C para obter uma strcmp()classificação semelhante a:

$ 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

Observe que algumas localidades podem causar algumas confusões, mesmo para strings totalmente ASCII e todos alnum. Como os tchecos (pelo menos em sistemas GNU), onde chestá umelemento de agrupamentoisso classifica depois h:

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

Ou, como apontado por @ninjalj, ainda mais estranhos em localidades húngaras:

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

Em zsh, você pode escolher a classificação comeliminatórias globais. Por exemplo:

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.

O tipo numérico echo *(n)também pode ser habilitado globalmente com a numericglobsortopção:

$ 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

Se você (como eu) está confuso com essa ordem naquele caso específico (aqui usando minha localidade britânica), consulteaquipara detalhes.

Responder2

A página de manual do bash especifica:

Expansão do nome do caminho

Após a divisão de palavras, a menos que a -fopção tenha sido definida, o bash verifica cada palavra em busca dos caracteres *, ?e [. Se um desses caracteres aparecer, então a palavra é considerada um padrão e substituída por uma lista ordenada alfabeticamente de nomes de arquivos que correspondem ao padrão [...].

Responder3

A menos que você acione algumas opções de shell muito específicas em alguns shells, a saída será garantidamente a mesma.

A ordem é especificada emo padrão POSIX:

Se o padrão corresponder a qualquer nome de arquivo ou caminho existente, o padrão deverá ser substituído por esses nomes de arquivo e caminho,classificados de acordo com a sequência de agrupamento em vigor na localidade atual. Se esta sequência de agrupamento não tiver uma ordem total de todos os caracteres (consulte XBD LC_COLLATE), quaisquer nomes de arquivos ou nomes de caminho que sejam agrupados igualmente deverão ser comparados byte a byte usando a sequência de agrupamento para o código do idioma POSIX.

Veja tambémCategoria LC_COLLATE na localidade POSIX, que resumidamente diz que se LC_COLLATE=C, então as coisas são ordenadas na ordem ASCII.


O bashmanual menciona

LC_COLLATE

Esta variável determina a ordem de agrupamento usada ao classificar os resultados da expansão do nome do caminho e determina o comportamento das expressões de intervalo, classes de equivalência e sequências de agrupamento na expansão do nome do caminho e na correspondência de padrões.

ksh93e zshpossui redação semelhante, o que me leva a crer que seguem o padrão POSIX nesse aspecto.

Outros shells gostam pdkshe dashnão dizem nada sobre a classificação dos nomes de arquivos resultantes da globbing de nomes de arquivos. Estou tentado a acreditar que isso significa que eles ainda seguem o mesmo padrão, pelo menos ao usar o código de idioma POSIX. Na minha experiência, não encontrei um shell que fizesse uma classificação abertamente "estranha" de nomes de arquivos ASCII.

Responder4

Se o objetivo principal é classificar os arquivos de entrada por idade, os mais antigos primeiro, você pode escrever

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

E se logs rotacionados e compactados também estiverem envolvidos:

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

informação relacionada