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

53

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

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

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

Desejo grepexaminar todos os arquivos da entrada de registro mais recente de um determinado tipo, esperando que catos arquivos juntos, como ...

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

No entanto, ocorreu-me que versões diferentes de bashou shou zshetc. podem ter idéias diferentes sobre como a *expansão é expandida.

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

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

Wossname
fonte
11
@ADDB A ordem de classificação padrão sorté a mesma da shell quando está expandindo um padrão de globbing de nome de arquivo.
Kusalananda
9
Essa é uma prática terrível de nomeação de arquivos. Por que você inicia sua execução com log (0) = - infty?
EP
14
@EP Nosso sistema de arquivos é um hiper-toróide complexo de 7 dimensões com numeração surreal dos inodes. Foi adquiridos com algum ramo obscuro de busybox e estamos sagacidade preso agora :)
Wossname
11
Você pode evitar catcom grep -h pattern /tmp/logs/log*suprimir nomes de arquivos anexados às correspondências. (Pelo menos com GNU grep, eu não verificar POSIX ou busybox.)
Peter Cordes
11
@Kusalananda Você já ouviu falar de uso inútil de cat, este é o uso inútil desort
gato

Respostas:

52

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

Pois sh, o POSIX exige que eles sejam classificados por meio de strcoll(), isto é, usando a ordem de classificação no código do idioma do usuário, como, lsembora alguns ainda o façam, com strcmp()base 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 observar acima que, para os shells que fazem a classificação com base na localidade, aqui em um sistema GNU com uma en_GB.UTF-8localidade, os -nomes dos arquivos são ignorados na classificação (a maioria dos caracteres de pontuação o faria). A óclassificação é ordenada de maneira mais esperada (pelo menos para o povo britânico), e o caso é ignorado (exceto quando se trata de decidir laços).

No entanto, você notará algumas inconsistências no log① log②. Isso ocorre porque a ordem de classificação de ① e ② não está definida nos locais GNU (atualmente; esperamos que seja corrigido algum dia). Eles têm a mesma classificação, para que você obtenha resultados aleatórios.

Alterar a localidade afetará a ordem de classificação. Você pode definir o código do idioma para 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 alguns códigos de idioma podem causar algumas confusões, mesmo para cadeias all-alnum all-ASCII. Como os tchecos (pelo menos nos sistemas GNU), ché um elemento de classificação que se classifica depois h:

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

Ou, como apontado por @ninjalj, ainda mais estranhos nos locais húngaros:

$ 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 com qualificadores glob . 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.

A ordenação numérica echo *(n)também pode ser ativada 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 estava) está confuso com essa ordem nessa instância específica (aqui usando meu local britânico), consulte aqui para obter detalhes.

Stéphane Chazelas
fonte
11
O caso 'ch' pode ser ainda mais estranho: alguns locais podem decidir que 'ch', 'Ch' e 'CH' são 1 elemento de intercalação cada, enquanto 'cH' são dois elementos de intercalação. Veja: unicode.org/cldr/trac/ticket/889 O CLDR atual não parece ser totalmente consistente: o húngaro atual ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) possui regras como &C<cs<<<Cs<<<CS, while &C<cs<<<cS<<<Cs<<<CSé marcado como um rascunho experimental proposto. A julgar por alguns dados mais antigos importados para o CLDR, o AIX e o MS mais antigos pareciam preferir a visualização "minúsculas e maiúsculas de dois elementos de agrupamento diferentes".
Ninjalj 1/06
E vi sistemas onde não funcionava de qualquer maneira. :(
Joshua
38

A página de manual do bash especifica:

Expansão do nome do caminho

Depois de repartição de palavras, a menos que a -fopção tiver sido definido, o bash verifica cada palavra para os personagens *, ?e [. Se um desses caracteres aparecer, a palavra será considerada um padrão e substituída por uma lista alfabética de nomes de arquivos que correspondem ao padrão […].

user4556274
fonte
11
Acabei de encontrar um bug interessante na manrenderização de texto ou de massa de vidraceiro ... se o texto que estou procurando for "digitado", o comando / search não o encontrará. Apenas maximizei meu terminal e aí está :) #
Wossname
2
Você cobriu bash. Tho OP também estava interessado em "zsh etc."
Kusalananda
29

A menos que você ative algumas opções de shell muito específicas em alguns shells, é garantido que a saída seja a mesma.

A ordem é especificada no padrão POSIX :

Se o padrão corresponder a qualquer nome de arquivo ou nome de caminho existente, ele será substituído por esses nomes de arquivo e nome de caminho, classificados de acordo com a sequência de intercalação em vigor no código de idioma atual . Se essa sequência de intercalação não tiver uma ordem total de todos os caracteres (consulte XBD LC_COLLATE), quaisquer nomes de arquivos ou caminhos que intercalem da mesma forma devem ser comparados ainda mais, byte a byte, usando a sequência de intercalação para o código do idioma POSIX.

Consulte também LC_COLLATE Category no código do idioma POSIX , que em resumo diz que se LC_COLLATE=C, então, as coisas são ordenadas em ordem ASCII.


O bashmanual menciona

LC_COLLATE

Essa variável determina a ordem de intercalação 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 intercalação na expansão do nome do caminho e na correspondência de padrões.

ksh93e zshpossui uma formulação semelhante, o que me leva a acreditar que eles seguem o padrão POSIX a esse respeito.

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

Kusalananda
fonte
2
Veja a numericglobsortopção zshque afetaria a classificação. Embora eu prefira ativá-lo em uma base por glob do echo *(n)que ativar a opção globalmente.
Stéphane Chazelas
Um nitpick. O Bash, no modo padrão, NÃO é compatível com Posix.
precisa saber é o seguinte
@ fpmurphy1 Diga mais.
Kusalananda
@Kusalananda. O Bash nunca foi certificado como reclamação POSIX. Para obter "POSIX-conformidade" em Bash, você deve invocar Bash com a --posixopção de linha de comando ou executarset -o posix
fpmurphy
@ fpmurphy1 Sim, mas a classificação da expansão dos caracteres brilhantes do nome do arquivo não é afetada pelo posixmodo Bash . Veja gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Isso me leva a acreditar (espero) que a classificação é compatível com POSIX.
Kusalananda
1

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

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

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

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
Sultans of Swing
fonte
4
Foi mencionado que os carimbos de data e hora nos arquivos não eram confiáveis.
Kusalananda
3
@Kusalananda, isso mesmo, a nossa hora do sistema é geralmente considerado para ser um gerador de números aleatórios :)
Wossname