Quand la double cotation est-elle nécessaire?


120

Le vieux conseil était de doubler toute expression impliquant un mot $VARIABLE, du moins si on voulait qu’elle soit interprétée par le shell comme un seul élément, sinon tout espace dans le contenu $VARIABLEjetterait le shell.

Je comprends cependant que, dans les versions plus récentes des coques, la double cotation n’est plus toujours nécessaire (du moins aux fins décrites ci-dessus). Par exemple, dans bash:

% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory

En zshrevanche, les trois mêmes commandes aboutissent. Par conséquent, sur la base de cette expérience, il semble que, dans bash, on puisse omettre les guillemets intérieurs [[ ... ]], mais pas dans les [ ... ]arguments de ligne de commande, alors que, dans zsh, les guillemets doubles peuvent être omis dans tous ces cas.

Mais inférer des règles générales à partir d'exemples anecdotiques comme celui-ci est une proposition aléatoire. Il serait bon de voir un résumé des cas où la double cotation est nécessaire. Je suis surtout intéressé par zsh, bashet /bin/sh.


10
Votre comportement observé dans zsh dépend des paramètres et est influencé par l' SH_WORD_SPLIToption.
Ulrich Dangel


3
Par ailleurs, les noms de variables en majuscules sont utilisés par les variables ayant une signification pour le système d'exploitation et le shell; la spécification POSIX conseille explicitement d'utiliser des noms en minuscule pour les variables définies par l'application. (Bien que la spécification citée se concentre spécifiquement sur les variables d'environnement, les variables d'environnement et les variables shell partagent un espace de noms: la tentative de création d'une variable shell avec un nom déjà utilisé par une variable d'environnement remplace ce dernier). Voir pubs.opengroup.org/onlinepubs/009695399/basedefs/… , quatrième paragraphe.
Charles Duffy

Réponses:


128

Commencez par séparer zsh du reste. Ce n'est pas une question de vieux / moderne coquilles: zsh se comporte différemment. Les concepteurs de zsh ont décidé de le rendre incompatible avec les coques traditionnelles (Bourne, ksh, bash), mais plus facile à utiliser.

Deuxièmement, il est beaucoup plus facile d'utiliser des guillemets doubles tout le temps que de se rappeler quand ils sont nécessaires. Ils sont nécessaires la plupart du temps, vous devrez donc apprendre quand ils ne sont pas nécessaires, pas quand ils sont nécessaires.

En résumé, les guillemets sont nécessaires chaque fois qu’une liste de mots ou un motif est attendu . Ils sont facultatifs dans les contextes où une chaîne brute est attendue par l'analyseur.

Qu'est-ce qui se passe sans citations

Notez que sans guillemets doubles, il se passe deux choses.

  1. Tout d'abord, le résultat de l'expansion (la valeur de la variable pour une substitution de paramètre telle que ${foo}, ou la sortie de la commande pour une substitution de commande telle $(foo)) est divisé en mots partout où il contient des espaces.
    Plus précisément, le résultat de l'expansion est divisé en chaque caractère qui apparaît dans la valeur de la IFSvariable (caractère séparateur). Si une séquence de caractères de séparation contient un espace (espace, tabulation ou nouvelle ligne), l’espace est compté comme un seul caractère; des séparateurs d’espace non-blancs de début, de fin ou répétés mènent à des champs vides. Par exemple, avec IFS=" :", :one::two : three: :four produit des champs vides avant one, entre oneet two, et (un seul) entre threeet four.
  2. Chaque champ résultant du fractionnement est interprété comme un glob (motif générique) s'il contient l'un des caractères \[*?. Si ce modèle correspond à un ou plusieurs noms de fichier, il est remplacé par la liste des noms de fichiers correspondants.

Une expansion de variable non citée $fooest généralement connue sous le nom d’opérateur «split + glob», "$foo"qui prend simplement la valeur de la variable foo. Il en va de même pour la substitution de commande: "$(foo)"est une substitution de commande, $(foo)est une substitution de commande suivie de split + glob.

Où vous pouvez omettre les guillemets

Voici tous les cas auxquels je peux penser dans un shell Bourne-style où vous pouvez écrire une substitution de variable ou de commande sans guillemets doubles, et la valeur est interprétée littéralement.

  • Sur le côté droit d'une mission.

    var=$stuff
    a_single_star=*

    Notez que vous avez besoin des guillemets après export, car il s’agit d’une commande ordinaire, et non d’un mot clé. Ceci n'est vrai que dans certains shells tels que dash, zsh (dans l'émulation sh), yash ou posh; bash et ksh traitent tous les deux exportspécialement.

    export VAR="$stuff"
  • Dans une casedéclaration.

    case $var in 

    Notez que vous avez besoin de guillemets doubles dans un modèle de casse. Le fractionnement des mots ne se produit pas dans un modèle de casse, mais une variable non citée est interprétée comme un modèle, tandis qu'une variable entre guillemets est interprétée comme une chaîne littérale.

    a_star='a*'
    case $var in
      "$a_star") echo "'$var' is the two characters a, *";;
       $a_star) echo "'$var' begins with a";;
    esac
  • Entre doubles crochets. Les doubles crochets sont une syntaxe spéciale du shell.

    [[ -e $filename ]]

    Sauf que vous avez besoin de guillemets doubles lorsqu'un motif ou une expression régulière est attendu: à droite de =ou ==ou !=ou =~.

    a_star='a*'
    if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
    elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
    fi

    Vous avez besoin de guillemets doubles, comme d’habitude, entre crochets simples [ … ]car il s’agit d’une syntaxe de shell ordinaire (c’est une commande qui s’appelle à être appelée [). Voir crochets simples ou doubles

  • Dans une redirection dans des shells POSIX non interactifs (ni bash, ni ksh88).

    echo "hello world" >$filename

    Certains shells, lorsqu'ils sont interactifs, traitent la valeur de la variable comme un modèle générique. POSIX interdit ce comportement dans les shells non interactifs, mais quelques shells, y compris bash (sauf en mode POSIX) et ksh88 (y compris lorsqu'ils sont trouvés en tant que supposés POSIX shde certains Unices commerciaux comme Solaris) le font encore ( bashtente également de se scinder et la redirection échoue à moins que scission + jokers résultats exactement un mot), ce qui est la raison pour laquelle il vaut mieux citer les cibles de redirections dans un shscript dans le cas où vous souhaitez le convertir en un bashscénario un jour ou l' exécuter sur un système où shest non conforme sur ce point, ou il peut être provenir de coquilles interactives.

  • Dans une expression arithmétique. En fait, vous devez omettre les guillemets pour qu'une variable soit analysée comme une expression arithmétique.

    expr=2*2
    echo "$(($expr))"

    Cependant, vous avez besoin des guillemets autour de l’extension arithmétique car ils sont sujets au fractionnement des mots dans la plupart des shells, comme le requiert POSIX (!?).

  • Dans un indice de tableau associatif.

    typeset -A a
    i='foo bar*qux'
    a[foo\ bar\*qux]=hello
    echo "${a[$i]}"

Une substitution de variable et de commande sans guillemets peut être utile dans de rares cas:

  • Lorsque la valeur de la variable ou le résultat de la commande consiste en une liste de modèles globaux et que vous souhaitez développer ces modèles en vue de la liste des fichiers correspondants.
  • Lorsque vous savez que la valeur ne contient aucun caractère générique, celui-ci $IFSn'a pas été modifié et vous souhaitez le scinder en caractères d'espacement.
  • Lorsque vous souhaitez fractionner une valeur à un caractère donné: désactivez la fonction de regroupement avec set -f, définissez IFSle caractère de séparation (ou laissez-le seul pour fractionner les blancs), puis développez le développement.

Zsh

Dans zsh, vous pouvez omettre les guillemets doubles la plupart du temps, à quelques exceptions près.

  • $varne se développe jamais en plusieurs mots, mais il se développe en liste vide (par opposition à une liste contenant un seul mot vide) si la valeur de varest la chaîne vide. Contraste:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo

    De même, "${array[@]}"étend à tous les éléments du tableau, alors que $arrayseulement aux éléments non vides.

  • Le @drapeau de l' expansion des paramètres nécessite parfois des guillemets doubles autour de la substitution ensemble: "${(@)foo}".

  • La substitution de commande subit la division du champ si elle n'est pas entre guillemets: echo $(echo 'a'; echo '*')imprime a *(avec un seul espace), tandis que echo "$(echo 'a'; echo '*')"la chaîne de deux lignes non modifiée est imprimée. Utilisez "$(somecommand)"pour obtenir le résultat de la commande en un seul mot, sans fin de ligne finale. Utilisez "${$(somecommand; echo _)%?}"pour obtenir le résultat exact de la commande, y compris les nouvelles lignes finales. Utilisez "${(@f)$(somecommand)}"pour obtenir un tableau de lignes à partir de la sortie de la commande.


En fait, vous devez omettre les guillemets pour qu'une variable soit analysée comme une expression arithmétique. Pourquoi suis-je capable de faire fonctionner votre exemple avec des guillemets:echo "$(("$expr"))"
Cyker

Voici ce que man bashdit: L'expression est traitée comme si elle se trouvait entre guillemets doubles, mais les guillemets doubles entre parenthèses ne font pas l'objet d'un traitement particulier.
Cyker

4
De plus, pour toute personne intéressée, les noms officiels de split + glob sont le fractionnement des mots et le développement du chemin .
Cyker

3
Pour info - sur StackOverflow , quelqu'un a tiré le langage "facultatif quand une chaîne brute est attendue" dans cette réponse pour défendre de ne pas citer d'argument echo. Il serait peut-être intéressant d'essayer de rendre le langage encore plus explicite ("quand une chaîne brute est attendue par l'analyseur", peut-être?)
Charles Duffy

2
@ Charles Duffy Ugh, je n'avais pas pensé à cette lecture erronée. J'ai remplacé «où» par «quand» et renforcé la phrase que vous avez suggérée.
Gilles
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.