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 alias
o set
o 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¹ ( sort
ordina 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 -Mlocale
argomento a perl
. Per l'ordinamento numerico (più simile a GNU sort -g
in quanto supporta numeri come +3
, 1.2e-5
e non migliaia di separatori, sebbene non esadimali), utilizzare <=>
invece di cmp
(e ancora -Mlocale
per il segno decimale dell'utente da onorare come per il sort
comando).
Sarai limitato dalla dimensione massima degli argomenti a un comando. Per evitarlo, potresti passare l'elenco dei file perl
sul 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 perl
l'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 -n
che non fa una differenza significativa durante il confronto 1.4
e 1.23
(in locali dove .
è il segno decimale) per esempio), aggiungi il n
qualificatore 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 pdf
file 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 awk
prima di passare a sort
e trasformarle indietro dopo l'ordinamento.