Ao contrário do ksh ou zsh, o bash não tem suporte interno para classificar matrizes ou listas de cadeias arbitrárias. Ele pode classificar globs ou a saída de alias
or set
or typeset
(embora os últimos 3 não estejam na ordem de classificação do código do idioma do usuário), mas isso não pode ser usado praticamente aqui.
Não há nada no baú da ferramenta POSIX que possa classificar prontamente listas arbitrárias de seqüências de caracteres¹ ( sort
classifica linhas, apenas sequências curtas (LINE_MAX geralmente são mais curtas que PATH_MAX) de caracteres diferentes de NUL e newline, enquanto os caminhos de arquivo são sequências de bytes não vazias, que 0).
Portanto, embora você possa implementar seu próprio algoritmo de classificação em awk
(usando o <
operador de comparação de cadeias) ou mesmobash
(usando [[ < ]]
), para caminhos arbitrários em bash
, de maneira portátil, o mais fácil pode ser o de perl
:
Com bash4.4+
, você pode fazer:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Isso dá uma strcmp()
ordem semelhante. Para uma ordem baseada em regras de agrupamento da localidade como em bolhas ou a saída de ls
, adicione um -Mlocale
argumento para perl
. Para classificação numérica (mais parecida com o GNU sort -g
, pois suporta números como +3
, 1.2e-5
e não milhares de separadores, embora não hexadimais), use em <=>
vez de cmp
(e novamente -Mlocale
para que a marca decimal do usuário seja honrada como para o sort
comando).
Você ficará limitado pelo tamanho máximo de argumentos a um comando. Para evitar isso, você pode passar a lista de arquivos para perl
seu stdin em vez de via argumentos:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Nas versões mais antigas do bash
, você pode usar um while IFS= read -rd ''
loop em vez de readarray -d ''
ou obter perl
a lista de caminhos citados corretamente, para que possa transmiti-lo eval "array=($(perl...))"
.
Com zsh
, você pode falsificar uma expansão global para a qual você pode definir uma ordem de classificação:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Com reply=($filearray)
isso, forçamos a expansão glob (que inicialmente era justa /
) a ser os elementos da matriz. Em seguida, definimos a ordem de classificação a ser baseada na cauda do nome do arquivo.
Para uma strcmp()
ordem semelhante, fixe o código do idioma para C. Para a classificação numérica (semelhante ao GNU sort -V
, não o sort -n
que faz uma diferença significativa ao comparar 1.4
e 1.23
(em locais onde .
é a marca decimal), por exemplo), adicione o n
qualificador glob.
Em vez de oe{expression}
, você também pode usar uma função para definir uma ordem de classificação como:
by_tail() REPLY=$REPLY:t
ou mais avançados como:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(so a/foo2bar3.pdf
(2,3 números) classifica depois de b/bar1foo3.pdf
(1,3) mas antes de c/baz2zzz10.pdf
(2,10)) e usa como:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Obviamente, eles podem ser aplicados em globs reais, pois é para isso que eles se destinam principalmente. Por exemplo, para uma lista de pdf
arquivos em qualquer diretório, classificados por nome de base / cauda:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Se a strcmp()
classificação baseada em uma é aceitável, e para cadeias curtas, você pode transformar as cadeias em sua codificação hexadecimal awk
antes de passar para sort
e transformar novamente após a classificação.