GNU/Linux >> Tutoriels Linux >  >> Linux

Quand mettre des guillemets autour d'une variable shell ?

Règle générale :mettez-le entre guillemets s'il peut soit être vide, soit contenir des espaces (ou n'importe quel espace blanc en fait) ou des caractères spéciaux (caractères génériques). Ne pas citer de chaînes avec des espaces conduit souvent le shell à diviser un seul argument en plusieurs.

$? n'a pas besoin de guillemets puisqu'il s'agit d'une valeur numérique. Que ce soit $URL Cela dépend de ce que vous autorisez et si vous voulez toujours un argument s'il est vide.

J'ai tendance à toujours citer les chaînes juste par habitude car c'est plus sûr de cette façon.


En bref, citez tout ce dont vous n'avez pas besoin que le shell effectue le fractionnement de jeton 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 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 un, ils ne seraient pas cités complètement textuellement.)

$ echo "There is no place like '$HOME'"
There is no place like '/home/me'

Aucun guillemet ne convient lorsque vous avez spécifiquement besoin que le shell effectue le fractionnement de jeton et/ou l'expansion des caractères génériques.

Fractionnement de jeton ;

 $ 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 générique :

$ 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 , non plus !)

En termes plus concrets, tout ce qui contient un nom de fichier doit généralement être entre guillemets (car les noms de fichiers peuvent contenir des espaces et d'autres métacaractères du shell). Tout ce qui contient une URL doit généralement être entre guillemets (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 entre guillemets (idem idem). Tout ce qui contient des espaces blancs significatifs autres que des espaces simples entre des caractères autres que des espaces blancs doit être entre guillemets (car sinon, le shell transformera l'espace blanc en espaces simples et supprimera tout espace blanc de début ou de fin).

Lorsque vous savez qu'une variable ne peut contenir qu'une valeur qui ne contient aucun métacaractère shell, la mise entre guillemets est facultative. Ainsi, un $? sans guillemets est fondamentalement correct, car cette variable ne peut jamais contenir qu'un seul nombre. Cependant, "$?" est également correct, et recommandé pour la cohérence générale et l'exactitude (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 & sera analysé 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 se trouve dans une variable sans guillemets.) Pour une chaîne statique, les guillemets simples ont le plus de sens, bien que toute forme de guillemet 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 en dents de scie". 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 dos à dos, formant une longue chaîne unique après la tokenisation et la suppression des guillemets.

$ 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 bonne à savoir.

En aparté, les scripts ne doivent généralement pas utiliser ls pour rien. Pour développer un caractère générique, il suffit de... l'utiliser.

$ 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 spécifiquement bien avec plusieurs arguments. stat aussi. Mais le bouclage sur une correspondance générique est un problème courant, et souvent fait de manière incorrecte.)

Une variable contenant une liste de jetons à boucler ou un caractère générique à développer est moins fréquente, nous l'abrégeons donc parfois en "tout citer à moins que vous ne sachiez précisément ce que vous faites".


Voici une formule en trois points pour les devis en général :

Citations doubles

Dans les contextes où nous voulons supprimer le fractionnement et l'agrégation de 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.

Citations simples

Dans les littéraux de chaîne où nous voulons supprimer l'interpolation et le traitement spécial des barres obliques inverses. En d'autres termes, les situations où l'utilisation de guillemets doubles serait inappropriée.

Pas de guillemets

Dans des contextes où nous sommes absolument sûrs qu'il n'y a pas de problèmes de fractionnement ou de globalisation des mots ou si nous voulons un fractionnement et un globalisation des mots .

Exemples

Citations doubles

  • chaînes littérales avec espace blanc ("StackOverflow rocks!" , "Steve's Apple" )
  • expansions variables ("$var" , "${arr[@]}" )
  • substitutions de commandes ("$(ls)" , "`ls`" )
  • globs où le chemin du répertoire ou la partie du nom de fichier comprend des espaces ("/my dir/"* )
  • pour protéger les guillemets simples ("single'quote'delimited'string" )
  • Extension des paramètres bash ("${filename##*/}" )

Citations simples

  • les noms de commande et les arguments contenant des espaces
  • chaînes littérales nécessitant une interpolation pour être supprimées ( 'Really costs $$!' , 'just a backslash followed by a t: \t' )
  • pour protéger les guillemets doubles ('The "crux"' )
  • Les littéraux regex qui nécessitent une interpolation pour être supprimés
  • utiliser des guillemets shell pour les littéraux impliquant des caractères spéciaux ($'\n\t' )
  • utiliser des guillemets shell là où nous devons protéger plusieurs guillemets simples et doubles ($'{"table": "users", "where": "first_name"=\'Steve\'}' )

Pas de guillemets

  • autour des variables numériques standards ($$ , $? , $# etc.)
  • dans des contextes arithmétiques comme ((count++)) , "${arr[idx]}" , "${string:start:length}"
  • à l'intérieur du [[ ]] expression qui est exempte de problèmes de fractionnement et de globalisation des mots (c'est une question de style et les opinions peuvent varier considérablement)
  • où nous voulons diviser les mots (for word in $words )
  • où nous voulons globbing (for txtfile in *.txt; do ... )
  • où nous voulons ~ être interprété comme $HOME (~/"some dir" mais pas "~/some dir" )

Voir aussi :

  • Différence entre les guillemets simples et doubles dans Bash
  • Quelles sont les variables spéciales du shell avec le signe dollar ?
  • Citations et évasion - Bash Hackers' Wiki
  • Quand les guillemets doubles sont-ils nécessaires ?

Linux
  1. Comment échapper aux guillemets dans Shell ?

  2. Stockage de la sortie de la commande dans la variable Shell ?

  3. Pourquoi une variable est-elle visible dans un sous-shell ?

  4. Qu'est-ce qu'un bon mnémonique pour Shell Double Vs. Guillemets simples?

  5. Fractionnement des commandes longues dans les scripts Shell ?

Comment stocker une commande Linux en tant que variable dans un script shell

Expansion d'une variable Shell et effet de Glob et Split dessus ?

Pourquoi une seule barre oblique inversée s'affiche-t-elle lors de l'utilisation de guillemets ?

Explication de la commande d'exportation sous Linux

Qu'est-ce que le sous-shell sous Linux ?

Shell - Écrire le contenu d'une variable dans un fichier