Pourquoi l'astérisque [az] correspond-il aux nombres?

13

J'ai 3 répertoires sur le chemin actuel.

$ls
a_0db_data  a_clean_0db_data  a_clean_data
$ls a_*_data
a_0db_data:

a_clean_0db_data:

a_clean_data:

$ls a_[a-z]*_data
a_clean_0db_data:

a_clean_data:

Je m'attendais à ce que la dernière commande ls corresponde uniquement a_clean_data. Pourquoi cela correspondait-il aussi à celui qui le contenait 0?

bash --version
GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)
user13107
la source
2
Voir cette question pour en savoir plus sur la différence entre une expression régulière et un glob.
terdon
4
Donc, le fait de a_*_datacorrespondre à l'un de ces fichiers ne vous a pas surpris?
Cthulhu
@Cthulhu tu m'as eu!
user13107

Réponses:

29

La [a-z]partie n'est pas celle qui correspond au nombre; c'est le *. Vous pouvez confondre le globbing du shell et les expressions régulières .

Des outils comme grepaccepter différentes saveurs d'expressions rationnelles (de base par défaut, -Epour étendu, -Ppour l' expression rationnelle Perl )

Par exemple ( -vinverse le match)

$ ls a_[a-z]*_data | grep -v "[0-9]"
a_clean_data

Si vous souhaitez utiliser une expression rationnelle bash, voici un exemple sur la façon de tester si la variable $refest un entier:

re='^[0-9]+$'
if ! [[ $ref =~ $re ]] ; then
  echo "error"
fi
Sébastien
la source
Comment utiliser alors bash regex? (voir tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_01.html )
user13107
1
voir cette question
umläute
21

Le problème est donc: pourquoi a_[a-z]*_datacorrespond-il a_clean_0db_data?

Cela peut être divisé en quatre parties:

  • a_correspond au début de a_clean_0db_data, laissant clean_0db_dataà correspondre

  • [a-z]correspond à n'importe quel caractère de la plage a-z(par exemple c), laissant lean_0db_dataà correspondre

  • * correspond à n'importe quel nombre de caractères, par exemple lean_0db

  • _data correspond à la fin _data

Dans les expressions régulières, [a-z]*cela signifierait n'importe quel nombre de caractères (y compris zéro) dans la plage de a..z , mais vous avez affaire à un globbing shell, pas à des expressions régulières.

Si vous voulez des expressions régulières, quelques findimplémentations ont un -regexprédicat pour cela:

find . -maxdepth 1 -regex "^.*/a_[a-z]*_data$"

Le -maxdepthn'est là que pour limiter les résultats de la recherche au dossier dans lequel vous vous trouvez. L' expression régulière correspond au nom de fichier entier , j'ai donc ajouté un ^.*/pour correspondre au chemin-portion

umläute
la source
11

*dans les modèles shell correspond à 0 ou plusieurs caractères. Il ne faut pas le confondre avec l' *opérateur d'expression régulière qui signifie 0 ou plus de l'atome précédent .

Il n'y a pas d'équivalent de regexp *dans les modèles de shell de base. Cependant, divers shells ont des extensions pour cela.

  • ksha *(something):

    ls a_*([a-z])_data
  • vous pouvez avoir la même chose bashavec shopt -s extglobou zshavec setopt kshglob:

    shopt -s extglob
    ls a_*([a-z])_data
  • Dans zshavec extendedglobactivé, #est équivalent à regexp *:

    setopt extendedglob
    ls a_[a-z]#_data
  • Dans les versions récentes de ksh93, vous pouvez également utiliser des expressions régulières dans les globes. Ici avec des expressions régulières étendues :

    ls ~(E:a_[a-z]*_data)

Notez que cela [a-z]correspond à différentes choses selon les paramètres régionaux actuels. Il correspond en général que les 26 aà zlettres non-latin accentués dans leC paramètres régionaux. Dans d'autres pays, cela correspond généralement davantage et n'a pas toujours de sens. Pour faire correspondre une lettre dans votre région, vous pouvez préférer [[:alpha:]].

Stéphane Chazelas
la source
Pourriez-vous donner un exemple de [a-z]correspondance plus que les 26 lettres correspondent dans les paramètres régionaux C? Ce dont je me souviens de la dernière fois que j'ai regardé cela, tous les encodages pratiquement utilisés dans les variantes Unix avaient ISO-646 comme base (puis les 128 codes supérieurs étaient utilisés différemment, directement pour les caractères dans des encodages comme ISO-8859-X, combinés dans codages comme UTF-8 ou la famille EUC). Même AIX n'avait pas de paramètres régionaux EBCDIC (au moins aussi disponibles pour moi). Je me souviens avoir essayé de trouver si les normes POSIX / UNIX l'exigeaient, mais je ne me souviens pas du résultat.
AProgrammer
1
@AProgrammer, qui est indépendant de l'encodage, qui est basé sur l'ordre de tri (LC_COLLATE). [a-z]inclut généralement éou í(mais pas nécessairement ź) dans les paramètres régionaux où le jeu de caractères les a, que le point de code dans cet encodage soit entre celui de a et z ou non. Seul l'environnement local C garantit un ordre de tri basé sur la valeur du point de code. Voir cette autre réponse pour plus de détails.
Stéphane Chazelas
Ok, ce que j'ai manqué, c'est que la plage a été interprétée selon la séquence de classement actuelle.
AProgrammer