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 echomê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, echoimprime 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é 5et 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 bashl'explication des nullgloboptions 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 failglobpour obtenir un comportement plus utile similaire à celui des coques modernes comme zshou fish.
                    failglob. nullglobpeut provoquer des problèmes inattendus, par exemple lors du collage d'une URL qui se trouve avoir un ?.
                    nullglobpour 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 echoou 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 echone voit jamais [[:digit:]], il ne voit que 1 3. Vous pouvez le voir en action en exécutant set -xce qui affichera les commandes en cours d'exécution (exécutez-le set +xpour le désactiver à nouveau).
                    echone 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