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 aliasor setor 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¹ ( sortclassifica 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 -Mlocaleargumento para perl. Para classificação numérica (mais parecida com o GNU sort -g, pois suporta números como +3, 1.2e-5e não milhares de separadores, embora não hexadimais), use em <=>vez de cmp(e novamente -Mlocalepara que a marca decimal do usuário seja honrada como para o sortcomando).
Você ficará limitado pelo tamanho máximo de argumentos a um comando. Para evitar isso, você pode passar a lista de arquivos para perlseu 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 perla 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 -nque faz uma diferença significativa ao comparar 1.4e 1.23(em locais onde .é a marca decimal), por exemplo), adicione o nqualificador 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 pdfarquivos 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 awkantes de passar para sorte transformar novamente após a classificação.