Shell POSIX: `$` perd-il sa signification spéciale s'il s'agit du dernier caractère d'un mot?

17

Sur frêne, tiret et bash, quand je cours

$ echo ab$

il revient

ab$

Ce comportement est-il spécifié par POSIX ou s'agit-il simplement d'une convention courante dans les shells compatibles POSIX? Je n'ai rien trouvé sur la page Langage de commande du shell POSIX qui mentionne ce comportement.

Harold Fischer
la source
4
La meilleure question est " $ gagne- t-il une signification spéciale s'il s'agit du dernier caractère d'un mot?" Il n'y a pas de signification particulière unique attribuée à $; il est utilisé pour introduire des extensions multiples, mais distinctes, comme l'expansion des paramètres ${...}, la substitution de commandes $(...)et les expressions arithmétiques $((...)). Certains shells introduisent des contextes supplémentaires, comme kshla variante de substitution de commandes de x=${ echo foo; echo bar;}(qui diffère de la norme $(...)en n'exécutant pas les commandes dans un sous-shell).
chepner
@chepner Souhaitez-vous peser sur la différence d'opinion entre Issac et Michael Homer? Leurs réponses se contredisent explicitement
Harold Fischer
1
Je suis d'accord avec l'interprétation de Michael Homer; le shell ne commence même pas à s'inquiéter des extensions tant que l'analyse n'est pas terminée, donc dans le mot unique ab$, il n'y a pas de caractère suivant $, qu'il ait été "suivi" par un caractère nul dans la chaîne d'entrée d'origine ou un espace dans un cas comme echo ab$ foo; l'espace blanc non cité d'origine a été reconnu et supprimé après l'analyse.
chepner

Réponses:

10

$n'a pas de signification particulière en soi (essayez echo $), uniquement lorsqu'il est combiné avec un autre caractère après lui et formant une expansion, par exemple $var(ou ${var}) $(util),, $((1+2)).

Le $obtient sa signification "spéciale" en définissant une extension dans la norme POSIX sous la section Reconnaissance de jetons :

Si le caractère actuel est un $ou sans guillemets `, le shell doit identifier le début de tout candidat pour l'expansion des paramètres, la substitution de commandes ou l'expansion arithmétique à partir de leurs séquences de caractères d'introduction entre guillemets: $ou ${, $(ou `, et $((, respectivement. La coque doit lire une entrée suffisante pour déterminer la fin de l'unité à agrandir ( comme expliqué dans les sections citées).). Lors du traitement des caractères, si des instances d'extensions ou de citations sont trouvées imbriquées dans la substitution, le shell doit les traiter récursivement de la manière spécifiée pour la construction trouvée. Les caractères trouvés depuis le début de la substitution jusqu'à sa fin, permettant toute récursion nécessaire pour reconnaître les constructions incorporées, doivent être inclus non modifiés dans le jeton de résultat, y compris tous les opérateurs ou citations de substitution incorporés ou englobants. Le jeton ne doit pas être délimité à la fin de la substitution.

Donc, si $ne forme pas une expansion, d'autres règles d'analyse entrent en vigueur:

Si le caractère précédent faisait partie d'un mot, le caractère actuel doit être ajouté à ce mot.

Cela couvre votre ab$chaîne.

Dans le cas d'un seul $(le «nouveau mot» serait le $seul):

Le caractère actuel est utilisé comme début d'un nouveau mot.

La signification du mot généré contenant un $qui n'est pas une extension standard est explicitement définie comme non spécifiée par POSIX.

Notez également qu'il $s'agit du dernier caractère $$, mais qu'il s'agit également de la variable qui contient le PID du shell actuel. Dans bash, !$peut invoquer une expansion d'historique (le dernier argument de la commande précédente). Donc, en général, non, $n'est pas sans signification à la fin d'un mot non cité, mais à la fin d'un mot, cela ne signifie pas du moins une expansion standard.

Kusalananda
la source
7

Selon la situation exacte, cela n'est pas explicitement spécifié (les implémentations peuvent donc faire ce qu'elles veulent) ou doit se produire comme vous l'avez observé. Dans votre scénario exact echo ab$, POSIX rend obligatoire la sortie "ab $" que vous avez observée et elle n'est pas non spécifiée . Un résumé rapide de tous les différents cas est à la fin.

Il y a deux éléments: d'abord la tokenisation en mots, puis l'interprétation de ces mots.


Tokenisation

Tokenisation POSIX exige un $qui ne soit pas le début d'une valide expansion de paramètres , la substitution de commande , ou substitution arithmétique pour être considéré comme une partie littérale de l' WORDêtre construit jeton. En effet, la règle 5 ("Si le caractère actuel est un $ou non guillemets `, le shell doit identifier le début de tout candidat pour l'expansion des paramètres, la substitution de commandes ou l'expansion arithmétique à partir de leurs séquences de caractères non entre guillemets d'introduction: $ou ${, $(ou `, et $((, respectivement" ) ne s'applique pas, car aucune de ces extensions n'y est viable. L'extension des paramètres nécessite qu'un nom valide y apparaisse et qu'un nom vide ne soit pas valide.

Étant donné que cette règle ne s'appliquait pas, nous continuons de suivre jusqu'à ce que nous en trouvions une qui s'applique. Les deux candidats sont # 8 ("Si le caractère précédent faisait partie d'un mot, le caractère actuel doit être ajouté à ce mot.") Et # 10 ("Le caractère actuel est utilisé comme début d'un nouveau mot.") , qui s'appliquent à echo a$et echo $respectivement.

Il y a aussi un troisième cas de la forme echo a$+bqui tombe dans la même fissure, car ce +n'est pas le nom d'un paramètre spécial. Nous y reviendrons plus tard, car il déclenche différentes parties des règles.

La spécification requiert donc que le $soit considéré comme une partie du mot syntaxiquement, et il peut ensuite être traité ultérieurement.


Expansion des mots

Une fois que l'entrée a été analysée de cette manière, avec le $mot inclus, des extensions de mot sont appliquées à chacun des mots qui ont été lus. Chaque mot est traité individuellement .

Il est précisé que :

Si un «$» non cité est suivi d'un caractère qui n'est pas l'un des suivants:

  • Un caractère numérique
  • Le nom de l'un des paramètres spéciaux (voir Paramètres spéciaux )
  • Un premier caractère valide d'un nom de variable
  • A <left-curly-bracket>('{')
  • UNE <left-parenthesis>

le résultat n'est pas spécifié.

"Non spécifié" est un terme particulier qui signifie ici que

  1. Un shell conforme peut choisir n'importe quel comportement dans ce cas
  2. Une application conforme ne peut pas s'appuyer sur un comportement particulier

Dans votre exemple, echo ab$le $ n'est suivi d' aucun caractère , donc cette règle ne s'applique pas et le résultat non spécifié n'est pas appelé. Il n'y a tout simplement aucune expansion$ , donc il est littéralement présent et imprimé.

Lorsqu'il s'appliquer est dans notre troisième cas d' en haut . Ici est suivie , ce qui est un numéro, paramètre spécial ( , , , , , , , ou ), début d'une variable nom (ou un underscore alphabétique de l' ensemble portable de caractères ), ou l' un des supports. Dans ce cas, le comportement n'est pas spécifié: un shell conforme est autorisé à inventer un paramètre spécial appelé pour se développer , et une application conforme ne doit pas supposer que le shell ne le fait pas . Le shell peut également faire tout ce qu'il souhaite, y compris signaler une erreur.echo a$+b$+@*#?-$!0+

Par exemple, zsh, y compris dans son mode POSIX, interprète $+bcomme "est un bensemble de variables " et remplace 1 ou 0 à sa place. Il a également des extensions pour ~et =. Il s'agit d'un comportement conforme.

Un autre endroit où cela pourrait se produire est echo "a$ b". Encore une fois, le shell est autorisé à faire ce qu'il veut, et vous, en tant qu'auteur du script, devez échapper au $si vous voulez une sortie littérale. Si vous ne le faites pas, cela peut fonctionner, mais vous ne pouvez pas vous y fier. Il s'agit de la lettre absolue de la spécification, mais je ne pense pas que ce type de granularité ait été prévu ou envisagé.


En résumé

  • echo ab$: sortie littérale, entièrement spécifiée
  • echo a$ b: sortie littérale, entièrement spécifiée
  • echo a$ b$: sortie littérale, entièrement spécifiée
  • echo a$b: expansion du paramètre b, entièrement spécifié
  • echo a$-b: extension du paramètre spécial -, entièrement spécifié
  • echo a$+b: comportement non spécifié
  • echo "a$ b": comportement non spécifié

Pour un $à la fin d'un mot, vous êtes autorisé à vous fier au comportement et il doit être traité littéralement et transmis à la echocommande dans le cadre de son argument. Il s'agit d'une exigence de conformité sur le shell.

Michael Homer
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
terdon
@MichaelHomer Serait echo $également littéral et entièrement spécifié?
Harold Fischer
@HaroldFischer Oui
Michael Homer
Où faire echo "$"et echo "a b$"tomber?
Harold Fischer