Je voudrais demander:
Pourquoi est echo {1,2,3}
étendu à 1 2 3, ce qui est un comportement attendu, tandis que echo [[:digit:]]
revient [[:digit:]]
alors que je m'attendais à ce qu'il imprime tous les chiffres de 0
à 9
?
Je voudrais demander:
Pourquoi est echo {1,2,3}
étendu à 1 2 3, ce qui est un comportement attendu, tandis que echo [[:digit:]]
revient [[:digit:]]
alors que je m'attendais à ce qu'il imprime tous les chiffres de 0
à 9
?
Réponses:
Parce que ce sont deux choses différentes. Le {1,2,3}
est un exemple d' expansion du corset . La {1,2,3}
construction est développée par le shell , avant echo
même de la voir. Vous pouvez voir ce qui se passe si vous utilisez set -x
:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Comme vous pouvez le voir, la commande echo {1,2,3}
est étendue à:
echo 1 2 3
Cependant, [[:digit:]]
est une classe de caractères POSIX . Lorsque vous le donnez echo
, le shell le traite également en premier, mais cette fois, il est traité comme un glob de shell . cela fonctionne de la même manière que si vous exécutez echo *
qui imprimera tous les fichiers du répertoire courant. Mais [[:digit:]]
c'est un glob de shell qui correspondra à n'importe quel chiffre. Maintenant, en bash, si un glob de shell ne correspond à rien, il sera étendu à lui-même:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Si le glob correspond à quelque chose, cela sera imprimé:
$ echo /e*c
+ echo /etc
/etc
Dans les deux cas, echo
imprime simplement ce que le shell lui dit d'imprimer, mais dans le second cas, puisque le glob correspond à quelque chose ( /etc
), il est dit d'imprimer ce quelque chose.
Donc, comme vous n'avez pas de fichiers ou de répertoires dont le nom se compose exactement d'un chiffre (ce [[:digit:]]
qui correspondrait), le glob est étendu à lui-même et vous obtenez:
$ echo [[:digit:]]
[[:digit:]]
Maintenant, essayez de créer un fichier appelé 5
et d'exécuter la même commande:
$ echo [[:digit:]]
5
Et s'il y a plus d'un fichier correspondant:
$ touch 1 5
$ echo [[:digit:]]
1 5
Ceci est (en quelque sorte) documenté dans man bash
l'explication des nullglob
options qui désactive ce comportement:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Si vous définissez cette option:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
pour obtenir un comportement plus utile similaire à celui des coques modernes comme zsh
ou fish
.
failglob
. nullglob
peut provoquer des problèmes inattendus, par exemple lors du collage d'une URL qui se trouve avoir un ?
.
nullglob
pour démontrer que le motif est interprété comme un glob par le shell.
{1,2,3}
est l' expansion de l'accolade , elle s'étend aux mots énumérés sans égard à leur signification.
[...]
est un groupe de caractères, utilisé dans l' expansion du nom de fichier (ou caractère générique ou glob) de manière similaire à l'astérisque *
et au point d'interrogation ?
. Il correspond à tout caractère unique figurant dans la liste ou aux caractères qui sont membres de groupes nommés, comme [:digit:]
si ceux-ci sont répertoriés. Le comportement par défaut de la plupart des shells consiste à laisser le caractère générique tel quel s'il n'y a aucun fichier qui le corresponde.
(Notez que vous ne pouvez pas vraiment transformer un caractère générique / motif en l'ensemble de chaînes qu'il correspondrait. L'astérisque peut correspondre à n'importe quelle chaîne de n'importe quelle longueur, donc étendre n'importe quel modèle le contenant produirait une liste infinie de chaînes.)
Donc:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Mais reste:
$ bash -c 'echo {1,2,3}'
1 2 3
Les deux sont développés par le shell , peu importe si la commande que vous exécutez est ls
, ou echo
ou rm
. Notez également que si l'un d'eux est cité, ils ne seront pas développés:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
avant de le passer à echo
, donc echo
ne voit jamais [[:digit:]]
, il ne voit que 1 3
. Vous pouvez le voir en action en exécutant set -x
ce qui affichera les commandes en cours d'exécution (exécutez-le set +x
pour le désactiver à nouveau).
echo
ne recherche pas de fichiers, le shell le fait, avant d'exécuter le echo
.
{1,2,3}
(et par exemple {1..3}
sont des extensions d'accolade . Elles sont interprétées par le shell avant l'exécution de la commande.
[[:digit:]]
est un jeton de correspondance de modèle , mais vous ne l'utilisez pas dans un emplacement contenant des fichiers correspondant à ce modèle. Si vous utilisez une correspondance de modèle qui n'a aucune correspondance, elle se développe à elle-même:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3