GNU/Linux >> Tutoriels Linux >  >> Linux

Le Bash Star * Wildcard produit-il toujours une liste triée (ascendante) ?

J'ai un répertoire rempli de fichiers avec des noms comme logXX où XX est un nombre hexadécimal majuscule à deux caractères, complété par des zéros, tel que :

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

Généralement, il y aura moins de 20 ou 30 fichiers au total. La date et l'heure de mon système particulier ne sont pas fiables (un système intégré sans sources de temps NTP ou GPS fiables). Cependant, les noms de fichiers s'incrémenteront de manière fiable comme indiqué ci-dessus.

Je souhaite grep à travers tous les fichiers pour l'entrée de journal la plus récente d'un certain type, j'espérais cat les fichiers ensemble tels que…

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Cependant, il m'est apparu que différentes versions de bash ou sh ou zsh etc. peuvent avoir des idées différentes sur la façon dont le * est développé.

La man bash la page ne dit pas si oui ou non l'expansion de * serait une liste alphabétique ascendante des noms de fichiers correspondants. Cela semble augmenter chaque fois que je l'ai essayé sur tous les systèmes dont je dispose - mais est-ce un comportement DÉFINI ou simplement spécifique à la mise en œuvre ?

En d'autres termes, puis-je absolument compter sur cat /tmp/logs/log* concaténer tous mes fichiers journaux par ordre alphabétique ?

Réponse acceptée :

Dans tous les shells, les globs sont triés par défaut. Ils étaient déjà par le /etc/glob helper appelé par le shell de Ken Thompson pour étendre les globs dans la première version d'Unix au début des années 70 (et qui a donné son nom aux globs).

Pour sh , POSIX exige qu'ils soient triés au moyen de strcoll() , qui utilise l'ordre de tri dans les paramètres régionaux de l'utilisateur, comme pour ls bien que certains le fassent encore via strcmp() , basé uniquement sur des valeurs d'octets.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01
$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log②  log①  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log②
log①
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Vous pouvez remarquer ci-dessus que pour les shells qui effectuent un tri basé sur les paramètres régionaux, ici sur un système GNU avec un en_GB.UTF-8 locale, le - dans les noms de fichiers est ignoré pour le tri (la plupart des caractères de ponctuation le feraient). Le ó est trié d'une manière plus attendue (au moins pour les Britanniques), et la casse est ignorée (sauf lorsqu'il s'agit de décider des liens).

Cependant, vous remarquerez quelques incohérences pour log① log②. C'est parce que l'ordre de tri de ① et ② n'est pas défini dans les locales GNU (actuellement; espérons qu'il sera corrigé un jour). Ils trient de la même manière, vous obtenez donc des résultats aléatoires.

En relation :Traiter les descendants ?

La modification des paramètres régionaux affectera l'ordre de tri. Vous pouvez définir les paramètres régionaux sur C pour obtenir un strcmp() -comme tri :

$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01

Notez que certains paramètres régionaux peuvent causer des confusions même pour les chaînes tout-ASCII tout-alnum. Comme les tchèques (sur les systèmes GNU au moins) où ch est un élément d'assemblage qui trie après h :

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

Ou, comme l'a souligné @ninjalj, encore plus étranges dans les localités hongroises :

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

En zsh , vous pouvez choisir le tri avec des qualificateurs glob. Par exemple :

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

Le tri numérique de echo *(n) peut également être activé globalement avec le numericglobsort choix :

$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log① log② log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Si vous (comme moi) êtes confus par cet ordre dans ce cas particulier (ici en utilisant mes paramètres régionaux britanniques), voir ici pour plus de détails.


Linux
  1. La commande "eval" dans Bash ?

  2. La fête ?

  3. Dans un script bash, comment la commande Continuer fonctionne-t-elle avec les boucles intégrées ?

  4. Comment personnaliser Bash Autocomplete pour lister les fichiers dans un autre répertoire ?

  5. Que fait 'bash -c' ?

La liste des raccourcis clavier Bash utiles

Personnalisation du shell Bash

Bash For Loop – Le guide le plus pratique

Obtenir des cycles de processeur à l'aide de RDTSC - pourquoi la valeur de RDTSC augmente-t-elle toujours ?

Échappez un caractère étoile (*) dans bash

Que fait le -e dans un bash shebang ?