Pourquoi le comportement de la syntaxe `#!` N'est-il pas spécifié par POSIX?

17

À partir de la page Shell Command Language de la spécification POSIX:

Si la première ligne d'un fichier de commandes shell commence par les caractères "#!", Les résultats ne sont pas spécifiés.

Pourquoi le comportement de #!non spécifié par POSIX? Je trouve déconcertant que quelque chose d'aussi portable et largement utilisé ait un comportement non spécifié.

Harold Fischer
la source
1
Les normes laissent des choses non spécifiées pour ne pas lier les implémentations à des comportements particuliers. Par exemple, une "connexion" est "L'activité non spécifiée par laquelle un utilisateur accède au système."
Kusalananda
2
Étant donné que POSIX ne spécifie pas de chemins exécutables, une ligne de shebang est de toute façon intrinsèquement non portable; Je ne suis pas sûr que beaucoup serait gagné en le spécifiant malgré tout.
Michael Homer
1
@MichaelHomer, sûrement pas? La norme pourrait spécifier que la ligne contient un chemin à utiliser pour l'interpréteur, même sans indiquer ce que devrait être ce chemin.
ilkkachu
1
@HaroldFischer Sauf qu'il n'est pas interprété par le shell, il est interprété soit par le noyau du système d'exploitation (fait au moins sur Linux, qui peut réellement désactiver cette prise en charge lors de la construction), soit par la bibliothèque qui implémente la exec()fonction. Donc, la vérification par rapport à plusieurs coques ne vous dit pas vraiment à quel point il est portable.
Austin Hemmelgarn
2
@HaroldFischer En outre, même parmi les systèmes d'exploitation compatibles POSIX, le comportement n'est pas cohérent. Linux et macOS se comportent différemment: Linux ne tokenise pas complètement la ligne shebang par des espaces. macOS ne permet pas à l'interpréteur de script d'être un autre script. Voir aussi en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Réponses:

21

Je pense principalement parce que:

  • le comportement varie considérablement entre l'implémentation. Voir https://www.in-ulm.de/~mascheck/various/shebang/ pour tous les détails.

    Il pourrait cependant maintenant spécifier un sous-ensemble minimum de la plupart des implémentations de type Unix: comme #! *[^ ]+( +[^ ]+)?\n(avec uniquement des caractères du jeu de caractères de nom de fichier portable dans ces un ou deux mots) où le premier mot est un chemin absolu vers un exécutable natif, la chose n'est pas trop long et comportement non spécifié si l'exécutable est setuid / setgid, et l'implémentation a défini si le chemin de l'interpréteur ou le chemin du script est transmis argv[0]à l'interpréteur.

  • POSIX ne spécifie de toute façon pas le chemin des exécutables. Plusieurs systèmes ont des utilitaires pré-POSIX dans /bin/ /usr/binet ont les utilitaires POSIX ailleurs (comme sur Solaris 10 où se /bin/shtrouve un shell Bourne et celui POSIX est /usr/xpg4/bin; Solaris 11 l'a remplacé par ksh93 qui est plus conforme POSIX, mais la plupart des autres les outils en /binsont encore anciens non POSIX). Certains systèmes ne sont pas POSIX mais ont un mode / émulation POSIX. Tout ce que POSIX exige, c'est qu'il y ait un environnement documenté dans lequel un système se comporte POSIXly.

    Voir Windows + Cygwin par exemple. En fait, avec Windows + Cygwin, la transgression est honorée lorsqu'un script est invoqué par une application cygwin, mais pas par une application Windows native.

    Donc, même si POSIX a spécifié le mécanisme shebang, il ne pourrait pas être utilisé pour écrire des scripts POSIX sh/ sed/ awk... (notez également que le mécanisme shebang ne peut pas être utilisé pour écrire un script sed/ fiable awkcar il ne permet pas de passer une fin d'option marqueur).

Maintenant, le fait qu'il ne soit pas spécifié ne signifie pas que vous ne pouvez pas l'utiliser (eh bien, il dit que vous ne devriez pas commencer par la première ligne #!si vous vous attendez à ce qu'il ne s'agisse que d'un commentaire régulier et non d'une transe), mais POSIX ne vous donne aucune garantie si vous le faites.

D'après mon expérience, l'utilisation de shebangs vous donne plus de garantie de portabilité que l'utilisation de la façon de POSIX d'écrire des scripts shell: laissez la she-bang, écrivez le script dans la shsyntaxe POSIX et espérez que tout ce qui invoque le script invoque un POSIX conforme sh, ce qui est très bien si vous savez que le script sera invoqué dans le bon environnement par le bon outil mais pas autrement.

Vous devrez peut-être faire des choses comme:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Si vous souhaitez être portable sur Windows + Cygwin, vous devrez peut-être nommer votre fichier avec une extension .batou .ps1et utiliser une astuce similaire pour cmd.exeou powershell.exeinvoquer le cygwin shsur le même fichier.

Stéphane Chazelas
la source
Fait intéressant, à partir du numéro 5 : "La construction #! Est réservée aux implémentations qui souhaitent fournir cette extension. Une application portable ne peut pas utiliser #! Comme première ligne d'un script shell; elle ne peut pas être interprétée comme un commentaire."
muru
@muru Si le script était vraiment portable, sur un système vraiment POSIX exécutant un POSIX sh, il n'aurait pas besoin d'une ligne de hachage car il serait exécuté par POSIX sh.
Kusalananda
1
@Kusalananda, ce n'est vrai que si execlpou execvpont été utilisés, non? Si je devais utiliser execve, cela entraînerait ENOEXEC?
muru
9

[L] e comportement semble cohérent entre tous les obus à réclamation POSIX. Je ne vois pas le besoin de besoin de marge de manœuvre ici.

Vous ne regardez pas assez profondément.

Dans les années 80, ce mécanisme n'était pas de facto standardisé. Bien que Dennis Ritchie l'ait mise en œuvre, cette mise en œuvre n'avait pas atteint le public du côté AT&T de l'univers. Il n'était effectivement accessible au public et connu qu'en BSD; avec des scripts shell exécutables non disponibles sur AT&T Unix. Il n'était donc pas raisonnable de le normaliser. La situation est illustrée par ce doco contemporain, l'un des nombreux:

Notez que BSD permet aux fichiers qui commencent par #! interpreterd'être exécutés directement, tandis que SysV permet uniquement aux fichiers a.out d'être exécutés directement. Cela signifie qu'une instance de l'une des exec…()routines d'un programme BSD peut devoir être modifiée sous SysV pour exécuter l'interpréteur (typliquement /bin/sh) pour ce programme à la place.
- Stephen Frede (1988). "Programmation sur System X Release Y". Bulletin d'information du groupe d'utilisateurs d'Unix Systems en Australie . Volume 9. Numéro 4. p. 111.

Un point important ici est que vous regardez les shells, alors que l'existence de scripts shell exécutables est en fait une question de exec…()fonctions. Ce que font les shells comprend les précurseurs du mécanisme de script exécutable, qui se trouvent encore dans certains shells encore aujourd'hui (et également de nos jours obligatoires pour le exec…p()sous - ensemble de fonctions), et est quelque peu trompeur. À cet égard, la norme doit aborder le fonctionnement exec…()d'un script interprété, et au moment de la création de POSIX, il ne fonctionnait tout simplement pas en premier lieu sur une grande partie du spectre des systèmes d'exploitation cibles .

Une question secondaire est de savoir pourquoi cela n'a pas été normalisé depuis, d'autant plus que le mécanisme du nombre magique pour les interprètes de script avait atteint le public du côté AT&T de l'univers et avait été documenté exec…()dans la définition d'interface du système 5 , au tournant des années 1990. :

Un fichier interprète commence par une ligne du formulaire

#! chemin d'accès [arg]
chemin est le chemin de l'interpréteur et arg est un argument facultatif. Lorsque vous executilisez un fichier d'interpréteur, le système execest l'interpréteur spécifié.
- exec. System V Interface Definition . Volume 1. 1991.

Malheureusement, le comportement reste aujourd'hui presque aussi largement différent qu'il l'était dans les années 80 et il n'y a pas de comportement vraiment commun à normaliser. Certains Unices (notamment HP-UX et FreeBSD, par exemple) ne prennent pas en charge les scripts comme interprètes de scripts. Que la première ligne soit un, deux ou plusieurs éléments séparés par des espaces varie entre MacOS (et les versions de FreeBSD avant 2005) et les autres. La longueur de chemin maximale prise en charge varie. et les caractères en dehors du jeu de caractères de nom de fichier portable POSIX sont délicats, tout comme les espaces de début et de fin. Ce que les 0e, 1er et 2e arguments finissent par être est également délicat, avec des variations importantes d'un système à l'autre. Certains sont actuellement conformes à POSIX mais non-Les systèmes Unix ne prennent toujours pas en charge un tel mécanisme, et le rendre obligatoire les convertirait en n'étant plus conforme à POSIX.

Lectures complémentaires

JdeBP
la source
1

Comme indiqué par certaines des autres réponses, les implémentations varient. Cela rend difficile la standardisation et la préservation de la compatibilité descendante avec les scripts existants. Cela est vrai même pour les systèmes POSIX modernes. Par exemple, Linux ne tokenise pas complètement la ligne shebang par des espaces. macOS ne permet pas à l'interpréteur de script d'être un autre script.

Voir également http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
la source