commande bash multi ligne avec commentaires après le caractère de continuation

29

Considérer

echo \ # this is a comment
foo

Cela donne:

$ sh foo.sh
 # this is a comment
foo.sh: line 2: foo: command not found

Après quelques recherches sur le web, j'ai trouvé une solution de DigitalRoss sur le site sœur Stack Overflow. On peut donc faire

echo `: this is a comment` \
foo

Ou bien

echo $(: this is a comment) \
foo

Cependant, DigitalRoss n'a pas expliqué pourquoi ces solutions fonctionnent. J'apprécierais une explication. Il a répondu avec un commentaire:

Il y avait auparavant une gotocommande shell qui se ramifiait aux étiquettes spécifiées comme :ici. Le gotoest parti mais vous pouvez toujours utiliser la : whateversyntaxe ... :est une sorte de commentaire analysé maintenant.

Mais j'aimerais plus de détails et de contexte, y compris une discussion sur la portabilité.

Bien sûr, si quelqu'un a d'autres solutions, ce serait bien aussi.

Voir aussi la question précédente Comment commenter des commandes multilignes dans des scripts shell? .


Retenez le message de la discussion ci-dessous. C'est `: this is a comment`juste une substitution de commande. La sortie de : this is a commentn'est rien, et cela se met à la place de `: this is a comment`.

Un meilleur choix est le suivant:

echo `# this is a comment` \
foo
Faheem Mitha
la source

Réponses:

25

Les commentaires se terminent à la première nouvelle ligne (voir la règle 10 de reconnaissance des jetons shell ), sans autoriser les lignes de continuation , donc ce code a foodans une ligne de commande distincte:

echo # this is a comment \
foo

Quant à votre première proposition, la barre oblique inverse n'est pas suivie d'une nouvelle ligne, vous ne faites que citer l'espace: c'est équivalent à

echo ' # this is a comment'
foo

$(: this is a comment)remplace la sortie de la commande : this is a comment. Si la sortie de cette commande est vide, il s'agit en fait d'une manière très déroutante d'insérer un commentaire au milieu d'une ligne.

Il n'y a pas de magie: :c'est une commande ordinaire, l' utilitaire deux - points , qui ne fait rien. L'utilitaire deux-points est surtout utile lorsque la syntaxe du shell nécessite une commande mais que vous n'avez rien à faire.

# Sample code to compress files that don't look compressed
case "$1" in
  *.gz|*.tgz|*.bz2|*.zip|*.jar|*.od?) :;; # the file is already compressed
  *) bzip2 -9 "$1";;
esac

Un autre cas d'utilisation est un idiome pour définir une variable si elle n'est pas déjà définie.

: "${foo:=default value}"

La remarque sur goto est historique. L'utilitaire colon date d'avant même le shell Bourne , jusqu'au shell Thompson , qui avait une instruction goto . Le colon signifiait alors une étiquette; un deux-points est une syntaxe assez courante pour les étiquettes goto (elle est toujours présente dans sed ).

Gilles 'SO- arrête d'être méchant'
la source
OK je vois. Y a-t-il une meilleure façon d'insérer des commentaires dans ce contexte que vous connaissez?
Faheem Mitha
3
@FaheemMitha Comme recommandé dans le fil que vous citez: décomposez votre commande en morceaux gérables et commentez chaque morceau. Si votre commande est si compliquée qu'elle nécessite un commentaire au milieu, il est temps de la simplifier!
Gilles 'SO- arrête d'être méchant'
1
Eh bien, la commande en question a beaucoup d'arguments ... Elle convertit un tas de fichiers vidéo en un seul fichier. Je ne vois pas de moyen direct de le simplifier. Peut-être créer une liste quelconque et la passer en argument? Je suppose que cela pourrait être une autre question.
Faheem Mitha
@FaheemMitha Exemple:, make_FINDun script quickie qui construit une longue liste d'arguments pour find. Ici, la motivation pour le construire morceau par morceau est que chaque morceau provient du corps d'une boucle, mais le même style permet de commenter chaque morceau.
Gilles 'SO- arrête d'être méchant'
1
Merci pour l'exemple. Mon exemple est fondamentalement juste une longue liste de noms donnés comme arguments à une commande. Je veux des commentaires attachés à chaque nom, car c'est le moyen le plus simple pour moi de garder le contexte. Je ne vois aucun moyen évident de diviser la commande en morceaux, et si je le faisais, cela pourrait la rendre encore plus longue, et c'est déjà assez long.
Faheem Mitha
6

Vous pouvez y parvenir en utilisant des tableaux Bash, par exemple

#!/bin/bash
CMD=(
  echo  # this is a comment
  foo
  )

"${CMD[@]}"

Cela définit un tableau, $CMDpuis le développe. Une fois développée, la ligne résultante est évaluée, dans ce cas, elle echo fooest exécutée.

Le texte entre (et )définit le tableau et est soumis à la syntaxe bash habituelle, donc tout sur une ligne après #est ignoré.

Remarque sur la conservation des espaces blancs entre guillemets

${CMD[@]}se développe en une seule chaîne qui est la concaténation de tous les éléments, séparés par un espace. Une fois développée, Bash analyserait alors la chaîne en jetons de la manière habituelle (cf. $ IFS ), ce qui n'est souvent pas ce que nous voulons.

En revanche, si l'expansion est entourée de guillemets doubles, c'est "${CMD[@]}"-à- dire que chaque élément du tableau est conservé. Considérez la différence entre hello world second itemet "hello world" "second item".

Exemple illustratif:

# LIST=("hello world" "second item")

# for ITEM in ${LIST[@]}; do echo $ITEM; done
hello
world
second
item

# for ITEM in "${LIST[@]}"; do echo $ITEM; done
hello world
second item
RobM
la source
Merci pour la réponse, @RobM. Donc, votre suggestion est d'inclure les commentaires / méta-informations sur les éléments de la liste dans le tableau bash lui-même? Cette question serait probablement plus utile si j'avais inclus un exemple du type de commande que j'essayais d'utiliser. Je ne sais pas pourquoi je ne l'ai pas fait.
Faheem Mitha
Bah, il s'avère que je n'ai pas lu attentivement votre question. Je pensais que vous posiez la question à laquelle vous étiez lié. Oops. :)
RobM
${CMD[@]}ne se développe pas en une seule chaîne. - Il s'étend à de nombreuses chaînes: pas seulement divisé pour chaque élément du tableau, mais également sur les espaces dans chaque élément. (C'est-à-dire: complètement inutile)
Robert Siemer
4

Ne fais pas ça $(: comment). Ce n'est pas un commentaire - c'est un sous-shell - un tout autre processus de shell pour la plupart des shells. Votre objectif est de faire moins avec votre contribution, pas plus, ce que cela ferait - même si c'est inutile.

Vous pouvez plutôt faire ...

printf '<%s>\n' some args here ${-##*"${--

                my long comment block

                }"}  and "more ${-##*"${--

                and another one in the
                middle of the quoted string
                there shouldn\'t b\e any special &
                (character) `echo issues here i hope >&2`
                basically anything that isn\'t a close \}
                $(echo the shell is looking for one >&2)
                }$(echo "}'"\" need backslash escaping >&2
                                )${-##*${--

                nesting is cool though

             }}"}here too"

}'" need backslash escaping
<some>
<args>
<here>
<and>
<more here too>

Fondamentalement, ce qui se passe, c'est que le shell effectue une substitution. Il remplace la valeur du paramètre shell spécial $-deux fois à chaque fois. C'est une chaîne courte de toute façon, mais elle est toujours définie - et donc la substitution interne - qui est interprétée comme un motif à supprimer de l'extérieur - ne se développe pas jusqu'au contenu entre les parenthèses lorsque j'utilise le -formulaire d'expansion.

Ici:

bash -x <<""
printf %s\\n '${-##*"'${-- a set param doesn\'t expand to this optional text }'"}'

+ printf '%s\n' '${-##*"hxB"}'
${-##*"hxB"}

Voir? Il est donc simplement agrandi deux fois. Dès que le shell trouve que le paramètre est défini, tout dans le champ d'extension optionnel est jeté, à peu près, et il se développe à sa valeur entière qui est supprimée de lui-même et donc à rien du tout. Dans la plupart des shells, vous n'avez même pas besoin d'échapper aux guillemets, mais vous en avez besoin bash.

Mieux encore:

COMMENT=
echo      ${COMMENT-"
           this will work the same way
           but the stripping isn\'t
           necessary because, while
           the variable is set, it is also
           empty, and so it will expand to
           its value - which is nothing
           "} this you\'ll see

this you'll see
mikeserv
la source