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 aliasou setou 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¹ ( sorttrie 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 -Mlocaleargument à perl. Pour le tri numérique (plus comme GNU sort -gcar il prend en charge des nombres comme +3, 1.2e-5et non pas des milliers de séparateurs, mais pas des hexadimaux), utilisez à la <=>place de cmp(et encore -Mlocalepour que la décimale de l'utilisateur soit respectée comme pour la sortcommande).
Vous serez limité par la taille maximale des arguments d'une commande. Pour éviter cela, vous pouvez passer la liste des fichiers perlsur 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 perlsortie 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 -nqui fait une différence significative lors de la comparaison 1.4et 1.23(dans les paramètres régionaux où se .trouve la marque décimale) par exemple), ajoutez le nqualificatif 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 pdffichiers 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 awkavant de passer à sortet reconvertir après le tri.