Dans cette question, quelqu'un signale un problème d'utilisation d'un document ici avec un mot délimiteur entre guillemets à l'intérieur de la $(...)
substitution de commande , où une barre oblique inversée \
à la fin d'une ligne à l'intérieur du document déclenche la poursuite de la ligne de jonction de nouvelle ligne , tandis que le même document ici à l' extérieur de la substitution de commande fonctionne comme prévu .
Voici un exemple de document simplifié:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
Cela comprend un backtick et un backslash à la fin d'une ligne. Le délimiteur est cité, donc aucune expansion ne se produit à l'intérieur du corps. Dans tous les Bourne-alikes, je trouve que cela génère le contenu textuellement. Si je mets le même document dans une substitution de commande comme suit:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
alors ils ne se comportent plus de manière identique:
dash
,ash
,zsh
,ksh93
, BusyBoxash
,mksh
et SunOS 5.10 Posixsh
donner tout le contenu in extenso du document, comme avant.- Bash 3.2 donne une erreur de syntaxe pour un backtick inégalé. Avec des raccourcis appariés, il tente d'exécuter le contenu en tant que commande.
- Bash 4.3 réduit "ghi" et "jkl" sur une seule ligne, mais n'a aucune erreur. L'
--posix
option n'affecte pas cela. Kusalananda me dit (merci!) Que lepdksh
comportement est le même .
Dans la question d'origine, j'ai dit que c'était un bug dans l'analyseur de Bash. C'est ça? [Mise à jour: oui ] Le texte pertinent de POSIX (tout de la définition du langage de commande Shell) que je peux trouver est:
- §2.6.3 Substitution de commande :
Avec le formulaire $ (commande), tous les caractères suivant la parenthèse ouvrante à la parenthèse fermante correspondante constituent la commande. Tout script shell valide peut être utilisé pour la commande , à l'exception d'un script composé uniquement de redirections qui produit des résultats non spécifiés.
- §2.7.4 Ici-Document :
Si une partie du mot est citée, le délimiteur doit être formé en effectuant la suppression des guillemets sur le mot et les lignes du document ne doivent pas être développées.
- §2.2.1 Caractère d'échappement (barre oblique inverse) :
Si un <newline> suit le <backslash>, le shell doit interpréter cela comme une continuation de ligne. Les <backslash> et <newline> doivent être supprimés avant de diviser l'entrée en jetons.
- §2.3 Reconnaissance des jetons :
Lorsqu'un jeton io_here a été reconnu par la grammaire (voir Shell Grammar ), une ou plusieurs des lignes suivantes immédiatement après le prochain jeton NEWLINE forment le corps d'un ou plusieurs documents ici et doivent être analysées selon les règles de Here- Document .
Lorsqu'il ne traite pas un io_here , le shell divisera son entrée en jetons en appliquant la première règle applicable ci-dessous au caractère suivant dans son entrée. ...
...
- Si le caractère actuel est <barre oblique inverse>, guillemet simple ou guillemet double et qu'il n'est pas cité, il affectera la citation des caractères suivants jusqu'à la fin du texte cité. Les règles de cotation sont celles décrites dans Devis . Au cours de la reconnaissance du jeton, aucune substitution ne doit être réellement effectuée, et le jeton de résultat doit contenir exactement les caractères qui apparaissent dans l'entrée (sauf pour la jonction <nouvelle>), non modifiés, y compris les guillemets ou opérateurs de substitution intégrés ou englobants, entre le et la fin du texte cité.
Mon interprétation de ceci est que tous les caractères après $(
la fin )
comprennent le script shell, textuellement; un document ici apparaît, donc le traitement ici-document a lieu au lieu de la tokenisation ordinaire; le document ici a alors un délimiteur entre guillemets, ce qui signifie que son contenu est traité textuellement; et le personnage d'évasion n'y entre jamais. Je peux voir un argument, cependant, que ce cas n'est tout simplement pas traité et que les deux comportements sont autorisés. Il est possible que j'ai également sauté un texte pertinent quelque part.
- Cette situation est-elle rendue plus claire ailleurs?
- Sur quoi un script portable devrait-il pouvoir s'appuyer (en théorie)?
- Le traitement spécifique donné par l'un de ces obus (Bash 3.2 / Bash 4.3 / tout le monde) est-il imposé par la norme? Interdit? Permis?
la source
echo "$x"
, mais toute façon d'inspecter la variable fonctionne. J'ai édité cette ligne en bas.$(...)
par quoi que ce soit la sortie ... Maintenant, lorsque vous exécutez la commande dans votre exemple dans un sous-shell (enbash
), il produit le résultat attendu. Ce n'est qu'en le transformant en substitution de commandes qu'il effondre "ghi" et "jkl". C'est donc un bug imoRéponses:
Cela a été demandé sur la liste de diffusion de Bash, et le responsable a confirmé qu'il s'agissait d'un bogue
Ils ont également mentionné que le texte de POSIX "n'est pas nécessairement ambigu, mais qu'il nécessite une lecture attentive". J'ai donc demandé des éclaircissements à ce sujet. Leur réponse, y compris une description du problème et l'interprétation de la norme, était la suivante:
la source