L'ancien conseil était de double-citer toute expression impliquant un $VARIABLE
, au moins si l'on voulait qu'il soit interprété par le Shell comme un seul élément, sinon, tous les espaces dans le contenu de $VARIABLE
jetterait le Shell.
Je comprends, cependant, que dans les versions plus récentes des shells, les guillemets doubles ne sont plus toujours nécessaires (au 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
Dans zsh
, en revanche, les trois mêmes commandes réussissent. Par conséquent, sur la base de cette expérience, il semble que, dans bash
, on puisse omettre les guillemets doubles à l'intérieur de [[ ... ]]
, mais pas à l'intérieur [ ... ]
ni 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 hasardeuse. Ce serait bien de voir un résumé des cas où des guillemets doubles sont nécessaires. Je suis principalement intéressé par zsh
, bash
et /bin/sh
.
Tout d'abord, séparez zsh du reste. Ce n'est pas une question de coquilles anciennes vs modernes: zsh se comporte différemment. Les concepteurs de zsh ont décidé de le rendre incompatible avec les shells traditionnels (Bourne, ksh, bash), mais plus simple d'utilisation.
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 un mot, les guillemets doubles sont nécessaires partout où une liste de mots ou un modèle est attendu . Ils sont facultatifs dans les contextes où une chaîne brute est attendue par l'analyseur.
Notez que sans guillemets doubles, deux choses se produisent.
${foo}
, Ou la sortie de la commande pour une substitution de commande comme $(foo)
) est divisé en mots partout où il contient un espace.IFS
(caractère séparateur). Si une séquence de caractères de séparation contient des espaces (espace, tabulation ou nouvelle ligne), les espaces sont comptés comme un seul caractère; les séparateurs non blancs en début, en fin ou répétés conduisent à des champs vides. Par exemple, avec IFS=" :"
, :one::two : three: :four
Produit des champs vides avant one
, entre one
et two
, et (un seul) entre three
et four
.\[*?
. Si ce modèle correspond à un ou plusieurs noms de fichiers, le modèle est remplacé par la liste des noms de fichiers correspondants.Une expansion de variable sans guillemets $foo
Est familièrement connue comme "l'opérateur split + glob", contrairement à "$foo"
Qui prend juste la valeur de la variable foo
. Il en va de même pour la substitution de commandes: "$(foo)"
est une substitution de commandes, $(foo)
est une substitution de commandes suivie de split + glob.
Voici tous les cas auxquels je peux penser dans un shell de style Bourne 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 affectation.
var=$stuff
a_single_star=*
Notez que vous avez besoin des guillemets doubles après export
, car il s'agit d'un code interne ordinaire, pas d'un mot clé. Cela n'est vrai que dans certains shells tels que dash, zsh (en émulation sh), yash ou chic; bash et ksh traitent tous les deux export
spécialement.
export VAR="$stuff"
Dans une instruction case
.
case $var in …
Notez que vous avez besoin de guillemets doubles dans un modèle de cas. Le fractionnement de mots ne se produit pas dans un modèle de casse, mais une variable sans guillemets 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 parenthèses doubles. Les crochets doubles sont une syntaxe spéciale Shell.
[[ -e $filename ]]
Sauf que vous avez besoin de guillemets doubles où un modèle ou une expression régulière est attendu: sur le côté droit 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 ils sont une syntaxe Shell ordinaire (c'est une commande qui se trouve être appelée [
). Voir crochets simples ou doubles
Dans une redirection dans des shells POSIX non interactifs (pas bash
, ni ksh88
).
echo "hello world" >$filename
Certains shells, lorsqu'ils sont interactifs, traitent la valeur de la variable comme un motif 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 comme (soi-disant) POSIX sh
de certains Unices commerciaux comme Solaris) le font toujours là (bash
essaie aussi splitting et la redirection échoue sauf si split + globbing donne exactement un mot), c'est pourquoi c'est mieux citer les cibles des redirections dans un script sh
au cas où vous voudriez le convertir en script bash
un jour, ou l'exécuter sur un système où sh
n'est pas conforme sur ce point, ou il peut être sourced à partir de shells interactifs.
À l'intérieur d'une expression arithmétique. En fait, vous devez laisser les guillemets pour qu'une variable soit analysée en tant qu'expression arithmétique.
expr=2*2
echo "$(($expr))"
Cependant, vous avez besoin des guillemets autour de l'expansion arithmétique car ils sont sujets au fractionnement de Word dans la plupart des shells comme POSIX l'exige (!?).
Dans un indice de tableau associatif.
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
Une variable sans guillemets et une substitution de commande peuvent être utiles dans de rares circonstances:
$IFS
N'a pas été modifié et vous souhaitez le diviser en espaces.set -f
, Définissez IFS
sur le caractère séparateur (ou laissez-le tranquille pour fractionner à l'espace), puis faites l'expansion.Dans zsh, vous pouvez omettre la plupart du temps les guillemets doubles, à quelques exceptions près.
$var
Ne se développe jamais sur plusieurs mots, mais il se développe sur la liste vide (par opposition à une liste contenant un seul mot vide) si la valeur de var
est 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[@]}"
S'étend à tous les éléments du tableau, tandis que $array
Ne s'étend qu'aux éléments non vides.
L'indicateur d'expansion du paramètre @
Nécessite parfois des guillemets doubles autour de la substitution entière: "${(@)foo}"
.
La substitution de commande subit une division de champ si elle n'est pas citée: echo $(echo 'a'; echo '*')
affiche a *
(Avec un seul espace) tandis que echo "$(echo 'a'; echo '*')"
imprime la chaîne non modifiée de deux lignes. Utilisez "$(somecommand)"
pour obtenir la sortie de la commande dans un seul mot, sans retour à la ligne final. Utilisez "${$(somecommand; echo _)%?}"
pour obtenir la sortie exacte de la commande, y compris les retours à la ligne finaux. Utilisez "${(@f)$(somecommand)}"
pour obtenir un tableau de lignes à partir de la sortie de la commande.