Ao contrário do ksh ou zsh, o bash não possui suporte embutido para ordenar matrizes ou listas de cadeias arbitrárias. Ele pode classificar globs ou a saída de alias
ou set
ou typeset
(embora os últimos 3 não estejam na ordem de classificação de localidade do usuário), mas isso não pode ser usado praticamente aqui.
Não há nada na ferramenta POSIX que possa ordenar prontamente listas arbitrárias de strings ¹ ( sort
classifica linhas, portanto, apenas sequências curtas (LINE_MAX sendo geralmente menores que PATH_MAX) de caracteres diferentes de NUL e newline, enquanto caminhos de arquivo não são -resvazie seqüências de bytes diferentes de 0).
Assim, você pode implementar seu próprio algoritmo de classificação em awk
(usando o operador de comparação <
string) ou mesmo bash
(usando [[ < ]]
), para caminhos arbitrários em bash
, portably, o mais fácil pode ser recorrer a perl
:
Com bash4.4+
, você poderia fazer:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Isso gera uma ordem semelhante a strcmp()
. Para um pedido com base nas regras de agrupamento do locale, como em globs ou na saída de ls
, inclua um argumento -Mlocale
em perl
. Para classificação numérica (mais como 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 o usuário marca decimal a ser honrada como para o comando sort
).
Você será limitado pelo tamanho máximo de argumentos para um comando. Para evitar isso, você poderia passar a lista de arquivos para perl
em seu stdin em vez de via argumentos:
readarray -td '' sorted_filearray < <(
printf '%ssorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Com versões anteriores de bash
, você poderia usar um while IFS= read -rd ''
em vez de readarray -d ''
ou obter perl
para exibir a lista de caminhos citados corretamente para poder passá-lo para eval "array=($(perl...))"
.
Com zsh
, você pode falsificar uma expansão glob para a qual você pode definir uma ordem de classificação:
by_tail() REPLY=$REPLY:t
Com reply=($filearray)
, na verdade, forçamos a expansão glob (que inicialmente era apenas /
) a ser os elementos da matriz. Em seguida, definimos a ordem de classificação com base na cauda do nome do arquivo.
Para uma ordem semelhante a strcmp()
, corrija a localidade para C. Para classificação numérica (semelhante a GNU sort -V
, não sort -n
, que faz uma diferença significativa ao comparar 1.4
e 1.23
(em locales onde .
é a marca decimal), por exemplo, adicione o qualificador n
glob.
Em vez de oe{expression}
, você também pode usar uma função para definir uma ordem de classificação como:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
ou mais avançados como:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
(então a/foo2bar3.pdf
(2,3 números) classifica após b/bar1foo3.pdf
(1,3) mas antes de c/baz2zzz10.pdf
(2,10))
e usar como:
pdfs=(**/*.pdf(N.oe+by_tail))
Naturalmente, eles podem ser aplicados em globos reais, pois é para isso que eles são destinados principalmente. Por exemplo, para uma lista de pdf
arquivos em qualquer diretório, classificados por basename / tail:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
¹ Se uma classificação strcmp()
-based for aceitável e para strings curtas, você poderá transformar as strings em sua codificação hexadecimal com awk
antes de passar para sort
e transformar novamente após a classificação.