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 $VARIABLE
jetterait 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 zsh
revanche, 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
, bash
et /bin/sh
.
SH_WORD_SPLIT
option.Réponses:
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.
${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
IFS
variable (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, avecIFS=" :"
,:one::two : three: :four
produit des champs vides avantone
, entreone
ettwo
, et (un seul) entrethree
etfour
.\[*?
. 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
$foo
est généralement connue sous le nom d’opérateur «split + glob»,"$foo"
qui prend simplement la valeur de la variablefoo
. 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.
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 deuxexport
spécialement.Dans une
case
déclaration.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.
Entre doubles crochets. Les doubles crochets sont une syntaxe spéciale du shell.
Sauf que vous avez besoin de guillemets doubles lorsqu'un motif ou une expression régulière est attendu: à droite de
=
ou==
ou!=
ou=~
.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 doublesDans une redirection dans des shells POSIX non interactifs (ni
bash
, niksh88
).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
sh
de certains Unices commerciaux comme Solaris) le font encore (bash
tente é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 unsh
script dans le cas où vous souhaitez le convertir en unbash
scénario un jour ou l' exécuter sur un système oùsh
est 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.
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.
Une substitution de variable et de commande sans guillemets peut être utile dans de rares cas:
$IFS
n'a pas été modifié et vous souhaitez le scinder en caractères d'espacement.set -f
, définissezIFS
le 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.
$var
ne 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 devar
est la chaîne vide. Contraste:De même,
"${array[@]}"
étend à tous les éléments du tableau, alors que$array
seulement 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 '*')
imprimea *
(avec un seul espace), tandis queecho "$(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.la source
echo "$(("$expr"))"
man bash
dit: 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.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?)