GNU/Linux >> Tutoriels Linux >  >> Linux

Quand les guillemets doubles sont-ils nécessaires ?

L'ancien conseil était de mettre entre guillemets toute expression impliquant un $VARIABLE , du moins si l'on voulait qu'il soit interprété par le shell comme un seul élément, sinon, tout espace dans le contenu de $VARIABLE jetterait la coquille.

Je comprends, cependant, que dans les versions plus récentes des shells, les guillemets doubles ne sont plus toujours nécessaires (au moins dans le but décrit 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 , 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 peut omettre les guillemets à l'intérieur de [[ ... ]] , mais pas à l'intérieur de [ ... ] ni dans les arguments de ligne de commande, alors que, dans zsh , les guillemets doubles peuvent être omis dans tous ces cas.

Mais déduire des règles générales à partir d'exemples anecdotiques comme ceux ci-dessus est une proposition hasardeuse. Ce serait bien de voir un résumé des moments où les doubles guillemets sont nécessaires. Je suis principalement intéressé par zsh , bash , et /bin/sh .

Réponse acceptée :

Tout d'abord, séparez zsh du reste. Ce n'est pas une question de shells anciens ou 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 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 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.

Que se passe-t-il sans guillemets

Notez que sans guillemets doubles, deux choses se produisent.

  1. Tout d'abord, le résultat de l'expansion (la valeur de la variable pour une substitution de paramètre comme ${foo} , ou la sortie de la commande pour une substitution de commande comme $(foo) ) est divisé en mots partout où il contient des espaces.
    Plus précisément, le résultat de l'expansion est divisé à chaque caractère qui apparaît dans la valeur de l'IFS variable (caractère séparateur). Si une séquence de caractères de séparation contient des espaces blancs (espace, tabulation ou retour à la ligne), l'espace blanc est compté comme un seul caractère ; les séparateurs non blancs de début, de 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 .
  2. Chaque champ résultant du fractionnement est interprété comme un glob (un modèle générique) s'il contient l'un des caractères [*? . 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 extension de variable sans guillemets $foo est familièrement connu sous le nom de "split+glob operator", contrairement à "$foo" qui prend juste 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 doubles

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.

  • À droite d'un devoir.

    var=$stuff
    a_single_star=*
    

    Notez que vous avez besoin des guillemets doubles après export , car il s'agit d'une fonction intégrée ordinaire, pas d'un mot-clé. Ceci n'est vrai que dans certains shells tels que dash, zsh (en émulation sh), yash ou posh; bash et ksh traitent tous deux export spécialement.

    export VAR="$stuff"
    
  • Dans un case déclaration.

    case $var in …
    

    Notez que vous avez besoin de guillemets doubles dans un modèle de casse. 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 alors 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 du shell.

    [[ -e $filename ]]
    

    Sauf que vous avez besoin de guillemets doubles là 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 comme d'habitude entre crochets simples [ … ] parce qu'ils sont en syntaxe shell ordinaire (c'est une commande qui s'appelle [ ). Voir Parenthèses 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'il est trouvé en tant que (soi-disant) POSIX sh de certains Unix commerciaux comme Solaris) le font toujours là (bash essaie également de fractionner et la redirection échoue à moins que split+globbing renvoie exactement un mot), c'est pourquoi il est préférable de citer les cibles des redirections dans un sh script au cas où vous voudriez le convertir en un bash script un jour, ou exécutez-le sur un système où sh n'est pas conforme sur ce point, ou il peut être sourcé à partir de coques interactives.

  • À l'intérieur d'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'expansion arithmétique car ils sont sujets à la division des mots 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 substitution de variable et de commande sans guillemets peut être utile dans de rares circonstances :

  • Lorsque la valeur de la variable ou la sortie de la commande consiste en une liste de modèles glob et que vous souhaitez étendre ces modèles à la liste des fichiers correspondants.
  • Lorsque vous savez que la valeur ne contient aucun caractère générique, que $IFS n'a pas été modifié et vous souhaitez le diviser en caractères d'espacement.
  • Lorsque vous souhaitez fractionner une valeur à un certain caractère :désactivez le globbing avec set -f , définissez IFS au caractère de séparation (ou laissez-le seul pour le diviser en espaces blancs), puis faites l'expansion.
En relation :Pourquoi utiliser le swap lorsqu'il y a plus qu'assez d'espace libre dans la RAM ?

Zsh

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

  • $var ne s'étend jamais à plusieurs mots, mais il s'étend à 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.

  • Le @ l'indicateur d'extension de paramètre nécessite parfois des guillemets doubles autour de la substitution entière :"${(@)foo}" .

  • La substitution de commande subit un fractionnement de champ si elle n'est pas entre guillemets :echo $(echo 'a'; echo '*') imprime a * (avec un seul espace) alors que echo "$(echo 'a'; echo '*')" imprime la chaîne de deux lignes non modifiée. Utilisez "$(somecommand)" pour obtenir la sortie de la commande en un seul mot, sans nouvelle ligne finale. Utilisez "${$(somecommand; echo _)%?}" pour obtenir la sortie exacte 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 relation:typeset -A donne une erreur dans le script ?
Linux
  1. Pourquoi le tilde (~) ne se développe-t-il pas à l'intérieur des guillemets doubles ?

  2. Pourquoi les pages de manuel Unix utilisent-elles des backticks doubles à la place des guillemets doubles ?

  3. Caractères génériques à l'intérieur des guillemets ?

  4. Envelopper une commande qui inclut des guillemets simples et doubles pour une autre commande ?

  5. Quand utiliser Nohup ?

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

Quand utiliser un serveur dédié

Suppression des guillemets simples et doubles dans une chaîne à l'aide des commandes Linux bash / standard uniquement

Ajouter des guillemets autour des champs dans la sortie du script AWK ?

comment supprimer les guillemets doubles dans un csv

Remplacer le trait de soulignement par une virgule et supprimer les guillemets doubles dans CSV