Le caractère générique Bash star * génère-t-il toujours une liste triée (par ordre croissant)?

53

J'ai un répertoire rempli de fichiers avec des noms comme logXXXX où est un nombre hexadécimal majuscule et zéro à deux caractères, 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. On ne peut pas compter sur la date et l’heure de mon système particulier (système intégré sans sources de temps NTP ou GPS fiables). Cependant, les noms de fichiers seront incrémentés de manière fiable, comme indiqué ci-dessus.

Je souhaite grepparcourir tous les fichiers de la dernière entrée de journal d'un certain type, j'espérais pouvoir catles regrouper, tels que ...

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

Cependant, il m'est apparu que différentes versions de bashor shou zshetc. pouvaient avoir des idées différentes sur la manière dont elles *sont développées.

La man bashpage ne dit pas si le développement de *serait une liste alphabétique définitivement ascendante des noms de fichiers correspondants. Il semble que chaque fois que je l’essaye, tous les systèmes dont je dispose sont en hausse, mais est-ce un comportement DEFINED ou une implémentation spécifique?

En d’autres termes, puis-je absolument compter sur la cat /tmp/logs/log*concaténation de tous mes fichiers journaux dans l’ordre alphabétique?

Wossname
la source
1
@ADDB L'ordre de tri par défaut sortest le même que celui utilisé pour le shell lorsqu'il étend un modèle de définition de nom de fichier.
Kusalananda
9
C'est une pratique de nommage de fichier terrible. Pourquoi commencez-vous votre parcours avec log (0) = - infty?
EP
14
@EP Notre système de fichiers est un hyper-toroïde complexe en 7 dimensions avec une numérotation surréaliste des inodes. Il était
protégé
1
Vous pouvez éviter catavec grep -h pattern /tmp/logs/log*pour supprimer les noms de fichiers préfixer aux matchs. (Au moins avec GNU grep, je n'ai pas vérifié POSIX ou busybox.)
Peter Cordes
1
@Kusalananda Vous avez entendu parler d'une utilisation inutile de cat, c'est une utilisation inutile desort
cat

Réponses:

52

Dans tous les shells, les globs sont triés par défaut. Ils étaient déjà par l' /etc/globassistant appelé par la coquille de Ken Thompson pour développer des globs dans la première version d'Unix au début des années 70 (et qui a donné leur nom à globs).

En effet sh, POSIX exige qu’ils soient triés par le biais de strcoll()l’ordre de tri dans les paramètres régionaux de l’utilisateur, comme c’est le cas pour lscertains, mais strcmp()uniquement via les 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 remarquerez peut-être ci-dessus que pour les shells qui effectuent un tri en fonction de la locale, ici sur un système GNU avec une en_GB.UTF-8locale, le -nom des fichiers est ignoré pour le tri (la plupart des caractères de ponctuation le seraient). Le óest trié de manière plus attendue (au moins pour les Britanniques), et le cas est ignoré (sauf quand il s'agit de décider des liens).

Cependant, vous remarquerez des incohérences dans log① log②. En effet, l'ordre de tri de et n'est pas défini dans les paramètres régionaux GNU (actuellement; espérons-le, il sera corrigé un jour). Ils trient la même chose, vous obtenez donc des résultats aléatoires.

Changer les paramètres régionaux affectera l'ordre de tri. Vous pouvez définir les paramètres régionaux sur C pour obtenir un strcmp()tri de type:

$ 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 (du moins sur les systèmes GNU) où se chtrouve un élément de classement qui trie après h:

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

Ou, comme l'a souligné @ninjalj, même les plus bizarres dans les localités hongroises:

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

Dans zsh, vous pouvez choisir le tri avec les qualificatifs 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 echo *(n)peut également être activé globalement avec l' numericglobsortoption:

$ 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 j’étais) confus par cet ordre dans ce cas particulier (ici, en utilisant mes paramètres régionaux britanniques), cliquez ici pour plus de détails.

Stéphane Chazelas
la source
1
Le cas 'ch' peut être encore plus étrange: certains paramètres régionaux peuvent décider que 'ch', 'Ch' et 'CH' sont 1 élément de classement chacun, alors que 'cH' sont deux éléments de classement. Voir: unicode.org/cldr/trac/ticket/889 Le CLDR actuel ne semble pas être tout à fait cohérent: le hongrois actuel ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) comporte des règles telles que &C<cs<<<Cs<<<CS, while &C<cs<<<cS<<<Cs<<<CSest marqué comme projet expérimental. À en juger par certaines données plus anciennes importées dans CLDR, les anciennes versions AIX et MS semblaient préférer la vue "minuscules puis majuscules sont deux éléments de classement différents".
ninjalj
Et j'ai vu des systèmes où cela ne fonctionnait pas de toute façon. :(
Joshua
38

La page de manuel de bash spécifie:

Expansion du chemin

Après la séparation de mots, à moins que l' -foption a été activée, le bash recherche dans chaque mot les caractères *, ?et [. Si l'un de ces caractères apparaît, le mot est considéré comme un motif et remplacé par une liste alphabétique de noms de fichiers correspondant au motif […].

utilisateur4556274
la source
1
Je viens de trouver un bogue intéressant dans le manrendu du texte, mastic ou ... Juste maximisé mon terminal et le
voilà
2
Vous avez couvert bash. Tho OP était également intéressé par "zsh etc."
Kusalananda
29

À moins que vous ne déclenchiez des options de shell très spécifiques dans certains shells, le résultat obtenu est identique.

La commande est spécifiée dans le standard POSIX :

Si le modèle correspond à des noms de fichier ou de chemin existants, il doit être remplacé par ces noms de fichier et noms de chemin, triés en fonction de la séquence de classement en vigueur dans les paramètres régionaux en cours . Si cette séquence de classement ne contient pas l'ordre total de tous les caractères (voir XBD LC_COLLATE), tous les noms de fichiers ou de chemins qui s'assemblent de manière égale doivent être comparés davantage, octet par octet, à l'aide de la séquence de classement pour les paramètres régionaux POSIX.

Voir aussi la catégorie LC_COLLATE dans la locale POSIX , qui dit en bref que si LC_COLLATE=C, alors les choses sont ordonnées dans l'ordre ASCII.


Le bashmanuel mentionne

LC_COLLATE

Cette variable détermine l'ordre de classement utilisé lors du tri des résultats du développement du chemin, ainsi que le comportement des expressions de plage, des classes d'équivalence et des séquences de classement lors du développement du chemin et de la correspondance de modèles.

ksh93et zsha une formulation similaire, ce qui me porte à croire qu'ils suivent la norme POSIX à cet égard.

D'autres shells, comme pdkshet dashne disent rien sur le tri des noms de fichiers résultant de la suppression de nom de fichier. Je suis tenté de croire que cela signifie qu'ils respectent toujours la même norme, du moins lorsqu'ils utilisent les paramètres régionaux POSIX. D'après mon expérience, je n'ai pas rencontré de shell qui effectue un tri ouvertement "étrange" des noms de fichiers ASCII.

Kusalananda
la source
2
Voir l' numericglobsortoption dans zshqui affecterait le tri. Cependant, je préférerais l'activer echo *(n)globalement, plutôt que d'activer l'option globalement.
Stéphane Chazelas
Un nitpick. Bash, en mode par défaut, n’est PAS conforme à Posix.
fpmurphy
@ fpmurphy1 Dites-en plus.
Kusalananda
@ Kusalananda. Bash n'a jamais été certifié en tant que plainte POSIX. Pour obtenir la "conformité POSIX" dans Bash, vous devez appeler Bash avec l' --posixoption de ligne de commande ou exécuterset -o posix
fpmurphy
@ fpmurphy1 Oui, mais le tri du développement des caractères de remplacement du nom de fichier n'est pas affecté par le posixmode de Bash . Voir gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Cela m'amène à croire (espérons plutôt) que le tri est conforme à POSIX.
Kusalananda
1

Si l’objectif principal est de trier les fichiers d’entrée par leur âge, en commençant par le plus vieux, vous pouvez écrire

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

Et si les journaux tournés et compressés sont également impliqués:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
Les sultans du swing
la source
4
Il a été mentionné que les horodatages sur les fichiers n'étaient pas fiables.
Kusalananda
3
@Kusalananda, c'est vrai, notre heure système est généralement considérée comme un générateur de nombres aléatoires :)
Wossname