Contrairement à ksh ou zsh, bash n'a pas de support intégré pour le tri des tableaux ou des listes de chaînes arbitraires. Il peut trier des globes ou la sortie de alias
ou set
ou typeset
(bien que ces 3 derniers ne soient pas dans l'ordre de tri des paramètres régionaux de l'utilisateur), mais cela ne peut pas être utilisé pratiquement ici.
Il n'y a rien dans le POSIX toolchest qui peut facilement trier des listes arbitraires de chaînes non plus¹ ( sort
trie les lignes, donc seules les séquences courtes (LINE_MAX étant souvent plus courtes que PATH_MAX) de caractères autres que NUL et retour à la ligne, tandis que les chemins de fichiers sont des séquences d'octets non vides autres que 0).
Ainsi, bien que vous puissiez implémenter votre propre algorithme de tri dans awk
(en utilisant l' <
opérateur de comparaison de chaînes) ou mêmebash
(en utilisant [[ < ]]
), pour des chemins arbitraires dans bash
, de manière portable, le plus simple peut être de recourir à perl
:
Avec bash4.4+
, vous pourriez faire:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Cela donne un strcmp()
ordre semblable à. Pour un ordre basé sur les règles de classement des paramètres régionaux comme dans globs ou la sortie de ls
, ajoutez un -Mlocale
argument à perl
. Pour le tri numérique (plus comme GNU sort -g
car il prend en charge des nombres comme +3
, 1.2e-5
et non pas des milliers de séparateurs, mais pas des hexadimaux), utilisez à la <=>
place de cmp
(et encore -Mlocale
pour que la décimale de l'utilisateur soit respectée comme pour la sort
commande).
Vous serez limité par la taille maximale des arguments d'une commande. Pour éviter cela, vous pouvez passer la liste des fichiers perl
sur son stdin au lieu d'arguments via:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Avec les anciennes versions de bash
, vous pourriez utiliser une while IFS= read -rd ''
boucle à la place de readarray -d ''
ou obtenir la perl
sortie de la liste des chemins correctement cités afin de pouvoir la transmettre eval "array=($(perl...))"
.
Avec zsh
, vous pouvez simuler une expansion globale pour laquelle vous pouvez définir un ordre de tri:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Avec, reply=($filearray)
nous forçons en fait l'expansion de glob (qui était initialement juste /
) pour être les éléments du tableau. Ensuite, nous définissons l'ordre de tri en fonction de la queue du nom de fichier.
Pour un strcmp()
ordre similaire, fixez les paramètres régionaux à C. Pour le tri numérique (similaire à GNU sort -V
, pas sort -n
qui fait une différence significative lors de la comparaison 1.4
et 1.23
(dans les paramètres régionaux où se .
trouve la marque décimale) par exemple), ajoutez le n
qualificatif glob.
Au lieu de oe{expression}
, vous pouvez également utiliser une fonction pour définir un ordre de tri comme:
by_tail() REPLY=$REPLY:t
ou plus avancés comme:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(donc a/foo2bar3.pdf
(2,3 nombres) trie après b/bar1foo3.pdf
(1,3) mais avant c/baz2zzz10.pdf
(2,10)) et utilise comme:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Bien sûr, ceux-ci peuvent être appliqués sur de vrais globes car c'est à cela qu'ils sont principalement destinés. Par exemple, pour une liste de pdf
fichiers dans n'importe quel répertoire, triés par nom de base / queue:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Si un strcmp()
tri basé sur est acceptable, et pour les chaînes courtes, vous pouvez transformer les chaînes en leur codage hexadécimal avec awk
avant de passer à sort
et reconvertir après le tri.