Contrariamente a ksh o zsh, bash non ha supporto incorporato per l'ordinamento di matrici o elenchi di stringhe arbitrarie. Può ordinare globs o l'output di aliaso seto typeset(sebbene quelli ultimi 3 non siano nell'ordinamento locale dell'utente), ma non possono essere usati praticamente qui.
Non c'è nulla nel toolchest POSIX che può facilmente ordinare elenchi arbitrari di stringhe¹ ( sortordina le linee, quindi solo brevi (LINE_MAX essendo spesso più brevi di PATH_MAX) sequenze di caratteri diversi da NUL e newline, mentre i percorsi dei file sono sequenze non vuote di byte altri di 0).
Quindi, mentre potresti implementare il tuo algoritmo di ordinamento in awk(usando l' <operatore di confronto delle stringhe) o anchebash (usando [[ < ]]), per percorsi arbitrari in bash, portabilmente, il più semplice potrebbe essere ricorrere a perl:
Con bash4.4+, potresti fare:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Questo dà un strcmp()ordine simile. Per un ordine basato sulle regole di confronto della locale come in globs o l'output di ls, aggiungi un -Mlocaleargomento a perl. Per l'ordinamento numerico (più simile a GNU sort -gin quanto supporta numeri come +3, 1.2e-5e non migliaia di separatori, sebbene non esadimali), utilizzare <=>invece di cmp(e ancora -Mlocaleper il segno decimale dell'utente da onorare come per il sortcomando).
Sarai limitato dalla dimensione massima degli argomenti a un comando. Per evitarlo, potresti passare l'elenco dei file perlsul suo stdin invece che tramite argomenti:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Con le versioni precedenti di bash, è possibile utilizzare un while IFS= read -rd ''ciclo anziché readarray -d ''o ottenere perll'output dell'elenco di percorsi correttamente citato in modo da poterlo passare eval "array=($(perl...))".
Con zsh, puoi simulare un'espansione glob per la quale puoi definire un ordinamento:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Con reply=($filearray)forziamo l'espansione glob (che inizialmente era giusta /) per essere gli elementi dell'array. Quindi definiamo l'ordinamento in base alla coda del nome file.
Per un strcmp()ordine simile, fissa le impostazioni locali su C. Per l'ordinamento numerico (simile a GNU sort -V, sort -nche non fa una differenza significativa durante il confronto 1.4e 1.23(in locali dove .è il segno decimale) per esempio), aggiungi il nqualificatore glob.
Invece di oe{expression}, puoi anche usare una funzione per definire un ordinamento come:
by_tail() REPLY=$REPLY:t
o più avanzati come:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(quindi a/foo2bar3.pdf(2,3 numeri) ordina dopo b/bar1foo3.pdf(1,3) ma prima c/baz2zzz10.pdf(2,10)) e utilizza come:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Naturalmente, quelli possono essere applicati su globs reali in quanto è principalmente destinato. Ad esempio, per un elenco di pdffile in qualsiasi directory, ordinati per basename / tail:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Se un strcmp()ordinamento basato su dati è accettabile e per stringhe brevi, è possibile trasformare le stringhe nella loro codifica esadecimale con awkprima di passare a sorte trasformarle indietro dopo l'ordinamento.