Quelqu'un pourrait-il me dire si je devrais ou non entourer des guillemets des variables dans un script shell?
Par exemple, ce qui suit est-il correct:
xdg-open $URL
[ $? -eq 2 ]
ou
xdg-open "$URL"
[ "$?" -eq "2" ]
Et si oui, pourquoi?
Quelqu'un pourrait-il me dire si je devrais ou non entourer des guillemets des variables dans un script shell?
Par exemple, ce qui suit est-il correct:
xdg-open $URL
[ $? -eq 2 ]
ou
xdg-open "$URL"
[ "$?" -eq "2" ]
Et si oui, pourquoi?
Réponses:
Règle générale: citez-le s'il peut être vide ou contenir des espaces (ou tout autre espace en fait) ou des caractères spéciaux (jokers). Ne pas citer de chaînes avec des espaces conduit souvent le shell à séparer un seul argument en plusieurs.
$?
n'a pas besoin de guillemets car il s'agit d'une valeur numérique. Qu'il s'agisse$URL
besoin dépend de ce que vous permettez là - bas et si vous voulez encore un argument si elle est vide.
J'ai tendance à toujours citer des chaînes juste par habitude car c'est plus sûr de cette façon.
IFS=0
, alors echo $?
peut être très surprenant.
cp $source1 $source2 $dest
, mais si pour une raison inattendue dest
n'est pas défini, le troisième argument disparaît tout simplement, et il se recopiera silencieusement source1
au source2
lieu de vous donner un erreur appropriée pour la destination vide (comme ce serait le cas si vous aviez cité chaque argument).
quote it if...
a le processus de réflexion à l'envers - les citations ne sont pas quelque chose que vous ajoutez quand vous en avez besoin, c'est quelque chose que vous supprimez quand vous en avez besoin. Mettez toujours les chaînes et les scripts entre guillemets simples, à moins que vous n'ayez besoin d'utiliser des guillemets doubles (par exemple pour laisser une variable se développer) ou de ne pas utiliser de guillemets (par exemple pour faire une extension de nom de fichier).
En bref, citez tout ce qui ne nécessite pas que le shell effectue le fractionnement des jetons et l'expansion des caractères génériques.
Les guillemets simples protègent le texte entre eux textuellement. C'est l'outil approprié lorsque vous devez vous assurer que la coque ne touche pas du tout la corde. En règle générale, il s'agit du mécanisme de cotation de choix lorsque vous n'avez pas besoin d'interpolation de variable.
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Les guillemets doubles conviennent lorsqu'une interpolation variable est requise. Avec des adaptations appropriées, c'est également une bonne solution de contournement lorsque vous avez besoin de guillemets simples dans la chaîne. (Il n'y a pas de moyen simple d'échapper un guillemet simple entre guillemets simples, car il n'y a pas de mécanisme d'échappement à l'intérieur des guillemets simples - s'il y en avait, ils ne citeraient pas complètement textuellement.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Aucun guillemet n'est approprié lorsque vous avez spécifiquement besoin du shell pour effectuer le fractionnement de jetons et / ou l'expansion de caractères génériques.
Fractionnement de jetons;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
Par contre:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(La boucle ne s'exécute qu'une seule fois, sur la chaîne unique entre guillemets.)
$ for word in '$words'; do echo "$word"; done
$words
(La boucle ne s'exécute qu'une seule fois, sur la chaîne littérale entre guillemets simples.)
Extension de caractères génériques:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
Par contre:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Il n'y a pas de fichier nommé littéralement file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Il n'y a pas de fichier nommé $pattern
!)
En termes plus concrets, tout ce qui contient un nom de fichier doit généralement être entre guillemets (car les noms de fichier peuvent contenir des espaces et d'autres métacaractères du shell). Tout ce qui contient une URL doit généralement être cité (car de nombreuses URL contiennent des métacaractères shell comme ?
et&
). Tout ce qui contient une expression régulière doit généralement être cité (idem idem). Tout ce qui contient des espaces blancs significatifs autres que des espaces simples entre des caractères autres que des espaces doit être entre guillemets (car sinon, le shell réduira effectivement les espaces blancs en espaces simples et coupera tout espace de début ou de fin).
Lorsque vous savez qu'une variable ne peut contenir qu'une valeur qui ne contient pas de métacaractères shell, les guillemets sont facultatifs. Ainsi, un sans guillemets $?
est fondamentalement bien, car cette variable ne peut contenir qu'un seul nombre. cependant,"$?"
est également correct, et recommandé pour la cohérence et l'exactitude générale (bien que ce soit ma recommandation personnelle, pas une politique largement reconnue).
Les valeurs qui ne sont pas des variables suivent fondamentalement les mêmes règles, bien que vous puissiez également échapper à tous les métacaractères au lieu de les citer. Pour un exemple courant, une URL avec un &
dedans sera analysée par le shell en tant que commande d'arrière-plan, sauf si le métacaractère est échappé ou entre guillemets:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Bien sûr, cela se produit également si l'URL est dans une variable sans guillemets.) Pour une chaîne statique, les guillemets simples ont le plus de sens, bien que toute forme de citation ou d'échappement fonctionne ici.
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
Le dernier exemple suggère également un autre concept utile, que j'aime appeler "la citation à bascule". Si vous devez mélanger des guillemets simples et doubles, vous pouvez les utiliser côte à côte. Par exemple, les chaînes entre guillemets suivantes
'$HOME '
"isn't"
' where `<3'
"' is."
peuvent être collés ensemble dos à dos, formant une seule longue chaîne après la tokenisation et la suppression du devis.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Ce n'est pas très lisible, mais c'est une technique courante et donc bon à savoir.
En passant, les scripts ne devraient généralement pas être utilisés ls
pour quoi que ce soit. Pour développer un caractère générique, utilisez-le simplement.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(La boucle est complètement superflue dans ce dernier exemple; printf
fonctionne particulièrement bien avec plusieurs arguments stat
. Mais la boucle sur une correspondance générique est un problème courant et souvent mal effectuée.)
Une variable contenant une liste de jetons à boucler ou un caractère générique à développer est moins fréquemment vue, donc nous abrégons parfois en "tout citer à moins que vous ne sachiez précisément ce que vous faites".
Voici une formule en trois points pour les citations en général:
Double citation
Dans les contextes où nous voulons supprimer le fractionnement et la globalisation des mots. Également dans les contextes où nous voulons que le littéral soit traité comme une chaîne et non comme une expression régulière.
Guillemets simples
Dans les chaînes littérales où nous voulons supprimer l'interpolation et le traitement spécial des barres obliques inverses. En d'autres termes, des situations où l'utilisation de guillemets doubles serait inappropriée.
Pas de devis
Dans les contextes où nous sommes absolument sûrs qu'il n'y a pas de problèmes de fractionnement ou de globalisation de mots ou nous voulons un fractionnement et une globalisation de mots .
Exemples
Double citation
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Guillemets simples
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Pas de devis
$$
, $?
, $#
etc.)((count++))
, "${arr[idx]}"
,"${string:start:length}"
[[ ]]
expression intérieure qui est exempte de problèmes de fractionnement de mots et de globbing (c'est une question de style et les opinions peuvent varier considérablement)for word in $words
)for txtfile in *.txt; do ...
)~
être interprétés comme $HOME
( ~/"some dir"
mais pas "~/some dir"
)Voir également:
"ls" "/"
L'expression «tous les contextes de chaîne» doit être qualifiée plus soigneusement.
[[ ]]
, les guillemets importent du côté droit de =
/ ==
et =~
: cela fait la différence entre interpréter une chaîne comme un motif / regex ou littéralement.
$'...'
) devraient certainement avoir leur propre section.
"ls" "/"
au lieu du plus courant ls /
, et je considère cela comme un défaut majeur dans les lignes directrices.
case
:)
J'utilise généralement cité comme "$var"
pour plus de sécurité, sauf si je suis sûr qu'il $var
ne contient pas d'espace.
J'utilise $var
comme moyen simple de joindre des lignes:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Pour utiliser les variables dans le script shell, utilisez "" les variables entre guillemets comme celle citée signifie que la variable peut contenir des espaces ou des caractères spéciaux qui n'affecteront pas l'exécution de votre script shell. Sinon, si vous êtes sûr de ne pas avoir d'espaces ou de caractères spéciaux dans votre nom de variable, vous pouvez les utiliser sans "".
Exemple:
echo "$ url name" - (peut être utilisé à tout moment)
echo "$ url name" - (Ne peut pas être utilisé dans de telles situations donc prenez des précautions avant de l'utiliser)