Expansion de chaîne entre guillemets et sans guillemets

11
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

Je comprends pourquoi 1 diffère de 2. Mais pourquoi 3 donne-t-il une sortie différente de 2? Veuillez également expliquer la sortie. Comment fonctionnent les devis sur les nouvelles lignes?

ManuelSchneid3r
la source
1
Connexes: Quand faut-il citer deux fois?
Gilles 'SO- arrête d'être méchant'

Réponses:

19

Une variable sans guillemets (comme dans $var) ou une substitution de commande (comme dans $(cmd)ou `cmd`) est l' opérateur split + glob dans des shells de type Bourne.

Autrement dit, leur contenu est divisé en fonction de la valeur actuelle de la $IFSvariable spéciale (qui contient par défaut l'espace, la tabulation et les caractères de nouvelle ligne)

Et puis chaque mot résultant de ce fractionnement est soumis à la génération de nom de fichier (également connu sous le nom de globbing ou expansion de nom de fichier ), c'est-à-dire qu'ils sont considérés comme des modèles et sont développés dans la liste des fichiers qui correspondent à ce modèle.

Donc, dans for i in $(xrandr), $(xrandr)parce qu'il n'est pas entre guillemets, est divisé en séquences d'espaces, de tabulations et de caractères de nouvelle ligne. Et chaque mot résultant de ce fractionnement est vérifié pour les noms de fichiers correspondants (ou laissé tel quel s'il ne correspond à aucun fichier), et les forboucle tous.

Dans for i in "$(xrandr)", nous n'utilisons pas l'opérateur split + glob car la substitution de commandes est citée, donc il y a un passage dans la boucle sur une valeur: la sortie de xrandr(sans les caractères de fin de ligne qui commandent les bandes de substitution ).

Cependant, dans echo $i, $iest à nouveau sans guillemets, donc à nouveau le contenu de $iest divisé et soumis à la génération du nom de fichier et ceux-ci sont passés comme arguments séparés à la echocommande (et echosort ses arguments séparés par des espaces).

Donc, la leçon apprise:

  • si vous ne voulez pas le fractionnement de mots ou la génération de noms de fichiers , citez toujours les extensions de variables et les substitutions de commandes
  • si vous voulez le fractionnement de mots ou la génération de noms de fichiers , ne les citez pas mais définissez-les en $IFSconséquence et / ou activez ou désactivez la génération de noms de fichiers si nécessaire ( set -f, set +f).

En règle générale, dans votre exemple ci-dessus, si vous souhaitez parcourir la liste de mots séparés par des blancs dans la sortie de xrandr, vous devez:

  • laisser $IFSà sa valeur par défaut (ou la désactiver) pour diviser les blancs
  • Utilisez cette set -foption pour désactiver la génération de nom de fichier, sauf si vous êtes sûr de xrandrne jamais générer de caractères *ou ?ou [(qui sont des caractères génériques utilisés dans les modèles de génération de nom de fichier)

Et puis utilisez uniquement l'opérateur split + glob (ne laissez que la substitution de commandes ou l'expansion de variables non citée) dans la inpartie de la forboucle:

set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done

Si vous souhaitez parcourir les lignes (non vides) de la xrandrsortie, vous devez définir $IFSle caractère de nouvelle ligne:

IFS='
'
Stéphane Chazelas
la source
4

Une nouvelle ligne citée est une nouvelle ligne. echo "$1"Donne donc un seul argument de ligne de commande à echo, qui imprime ensuite directement les retours à la ligne.

Une nouvelle ligne sans guillemets est un espace. echo $1Donne donc de nombreux arguments de ligne de commande à echo, qui les affiche l'un après l'autre séparés par des espaces.

rici
la source
Une nouvelle ligne non citée est que les espaces sont un peu trop raccourcis pour être vraiment utiles comme explication du comportement ici à l'OMI.
Stéphane Chazelas