Quel interpréteur de shell exécute un script sans shebang?

17

Supposons que le shell par défaut de mon compte soit zsh mais j'ai ouvert le terminal et lancé bash et exécuté un script nommé prac002.sh, quel interpréteur de shell serait utilisé pour exécuter le script, zsh ou bash? Prenons l'exemple suivant:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDIT: ** Voici le contenu du script

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname
7_R3X
la source
Que dit le script en haut?
Michael Homer
@MichaelHomer: Rien. Je viens de modifier la question pour ajouter le contenu du script. Le script a une autorisation exécutable.
7_R3X du
Si vous souhaitez utiliser le shell courant, dot source, comme dans . prac002.sh, en supposant que votre script est dans le répertoire courant.
thecarpy
1
Exécutez . ./prac002.shet il fonctionnera avec le shell actuel, c'est-à-dire dot ( .), space () suivi du chemin de votre script. Cela s'appelle dot-sourcing votre script. ;-)
thecarpy

Réponses:

19

Parce que le script ne commence pas par une #!ligne shebang indiquant quel interprète utiliser, POSIX dit que :

Si la execl()fonction échoue en raison d'une erreur équivalente à l'erreur [ENOEXEC] définie dans le volume System Interfaces de POSIX.1-2008, le shell doit exécuter une commande équivalente à avoir un shell appelé avec le chemin d'accès résultant de la recherche comme premier opérande , avec tous les arguments restants passés au nouveau shell, sauf que la valeur de "$ 0" dans le nouveau shell peut être définie sur le nom de la commande. Si le fichier exécutable n'est pas un fichier texte, le shell peut contourner cette exécution de commande. Dans ce cas, il doit écrire un message d'erreur et retourner un état de sortie de 126.

Cette formulation est un peu ambiguë, et différents coques ont des interprétations différentes.

Dans ce cas, Bash exécutera le script en utilisant lui-même . D'un autre côté, si vous l'exécutiez à partir de zsh à la place, zsh utiliseraitsh (quoi que ce soit sur votre système) à la place.

Vous pouvez vérifier ce comportement pour ce cas en ajoutant ces lignes au script:

echo $BASH_VERSION
echo $ZSH_VERSION

Vous remarquerez que, à partir de Bash, la première ligne génère votre version, tandis que la seconde ne dit jamais rien, quel que soit le shell que vous utilisez.

  • Si votre /bin/shest, disons, dashalors aucune ligne ne sortira quoi que ce soit lorsque le script est exécuté à partir de zsh ou de dash.
  • Si votre /bin/shest un lien vers Bash, vous verrez la sortie de la première ligne dans tous les cas.
  • Si la version de Bash /bin/shest différente de celle que vous utilisiez directement, vous verrez une sortie différente lorsque vous exécutez le script directement depuis bash et depuis zsh.

La ps -p $$commande de la réponse de rools affichera également des informations utiles sur la commande utilisée par le shell pour exécuter le script.

Michael Homer
la source
J'ai voté positivement, cependant, il utilise la coquille par défaut si aucun shebang n'est spécifié, quel que soit le shell par défaut que vous avez spécifié. C'est pourquoi vous devez toujours, à mon humble avis, spécifier un shebang.
thecarpy
4
Ce n'est pas le cas, ce qui était vraiment tout le point de la réponse. Votre deuxième phrase est certainement vraie.
Michael Homer
vous avez raison, bash l'exécute en utilisant lui-même, le shell par défaut sur la plupart de mes boîtes se trouve être .... bash, à partir de là ma confusion. Je viens de tester sur Solaris 8 avec ksh comme shell par défaut, bien sûr, bash l'exécute comme bash ... j'ai appris quelque chose de nouveau aujourd'hui, merci (voté!) ;-)
thecarpy
Merci. L'échec de l' execl()appel se produit-il lorsqu'un script shell ne contient pas de shebang et est exécuté comme scriptname? Cela ne se produit-il pas lorsqu'un script shell est exécuté en tant que bash scriptname? Cela ne se produit-il pas lorsqu'un script shell contient un shebang et est exécuté comme scriptname?
StackExchange for All
J'ai fait un post à unix.stackexchange.com/questions/439376/…
StackExchange for All
7

Étant donné que le fichier ne fait partie d'aucun type d'exécutable reconnu par le système, et en supposant que vous avez la permission d'exécuter ce fichier, l' execve()appel système échoue généralement avec une erreur ENOEXEC( pas un exécutable ).

Ce qui se passe alors dépend de l'application et / ou de la fonction de bibliothèque utilisée pour exécuter la commande.

Cela peut être par exemple un shell, la fonction execlp()/ execvp()libc.

La plupart des autres applications utiliseront l'une de ces applications lorsqu'elles exécuteront une commande. Ils invoqueront un shell par exemple au moyen de la system("command line")fonction libc qui invoquera généralement shpour analyser cette ligne de commande (dont le chemin peut être déterminé au moment de la compilation (comme /bin/shvs /usr/xpg4/bin/shsur Solaris)), ou invoquer le shell stocké $SHELLpar eux-mêmes comme viavec sa !commande, et xterm -e 'command line'et de nombreuses autres commandes ( su user -cinvoquera le shell de connexion de l'utilisateur au lieu de $SHELL).

Généralement, un fichier texte sans shebang qui ne commence pas par #est considéré comme un shscript. Ce shqui variera cependant.

execlp()/ execvp(), lors du execve()retour, il ENOEXECsera généralement invoqué sh. Pour les systèmes qui en ont plus d'un shcar ils peuvent se conformer à plus d'une norme, shcelle-ci sera généralement déterminée au moment de la compilation (de l'application utilisant execvp()/ execlp()en liant un autre bloc de code qui fait référence à un chemin différent sh). Par exemple, sur Solaris, ce sera /usr/xpg4/bin/sh(un standard, POSIX sh) ou /bin/sh(le shell Bourne (un shell obsolète) sur Solaris 10 et les versions antérieures, ksh93 dans Solaris 11).

En ce qui concerne les coquilles, il y a beaucoup de variations. bash, AT&T ksh, le shell Bourne interprètera généralement le script lui-même (dans un processus enfant sauf s'il execest utilisé) après avoir simulé un execve(), c'est-à-dire annuler toutes les variables non exportées, fermer tous les fds close-on-exec, supprimer tous les pièges personnalisés, alias, fonctions ... ( bashinterprétera le script en shmode). yashs'exécutera (avec shcomme argv[0]ainsi en shmode) pour l'interpréter.

zsh, pdksh, ashCoquilles à base invoqueront généralement sh( dont le trajet déterminé au moment de la compilation).

Pour cshet tcsh(et pour shcertains des premiers BSD), si le premier caractère du fichier est #, ils s'exécuteront eux-mêmes pour l'interpréter, et shautrement. Cela remonte à une époque pré-shebang où l'on cshreconnaissait #comme commentaires mais pas le shell Bourne, donc #c'était un indice qu'il s'agissait d'un script csh.

fish(au moins la version 2.4.0), renvoie simplement une erreur en cas d' execve()échec (il ne tente pas de le traiter comme un script).

Certains shells (comme bashAT&T ksh) essaieront d'abord de déterminer de manière heuristique si le fichier est probablement destiné à être un script ou non. Vous pouvez donc constater que certains shells refuseront d'exécuter un script s'il contient un caractère NUL dans les premiers octets.

Notez également que si execve()échoue avec ENOEXEC mais que le fichier a une ligne de shebang, certains shells essaient d'interpréter cette ligne de shebang eux-mêmes.

Voici donc quelques exemples:

  • Quand $SHELLest /bin/bash, xterm -e 'myscript with args'aura myscriptinterprété par bashen shmode. Tandis qu'avec xterm -e myscript with args, xtermutilisera execvp()donc le script sera interprété par sh.
  • su -c myscriptsous Solaris 10 où rootest /bin/shet /bin/shest le shell de connexion, le shell Bourne aura été myscriptinterprété par le shell Bourne.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'sous Solaris 10, il sera interprété par /usr/xpg4/bin/sh(idem pour /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;sous Solaris 10 (en utilisant execvp()) le fera interpréter par /bin/sheven avec /usr/xpg4/bin/find, même dans un environnement POSIX (un bug de conformité).
  • csh -c myscriptl'aura interprété par cshs'il commence par #, shsinon.

Dans l'ensemble, vous ne pouvez pas être sûr du shell qui sera utilisé pour interpréter ce script si vous ne savez pas comment et par quoi il sera invoqué.

Dans tous les cas, la syntaxe read -pest bash-only, vous voudrez donc vous assurer que le script est interprété par bash(et éviter cette .shextension trompeuse ). Soit vous connaissez le chemin de l' bashexécutable et utilisez:

#! /path/to/bash -
read -p ...

Ou vous pouvez essayer de vous fier à une $PATHrecherche de l' bashexécutable (en supposant qu'il bashsoit installé) en utilisant:

#! /usr/bin/env bash
read -p ...

(se envtrouve presque partout dans /usr/bin). Alternativement, vous pouvez le rendre compatible POSIX + Bourne, auquel cas vous pouvez l'utiliser /bin/sh. Tous les systèmes auront un /bin/sh. Sur la plupart d'entre eux, il sera (pour la plupart) compatible POSIX, mais vous pouvez toujours y trouver de temps en temps un shell Bourne.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
Stéphane Chazelas
la source
1

Lorsque vous n'avez pas de ligne #!(appelée shebang ), sh est utilisé. Pour vérifier cela, vous pouvez exécuter le script suivant.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

Sur mon ordinateur, je reçois

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

même si mon shell par défaut est zsh . Il utilise bash car sur ma machine, la commande sh est implémentée par bash .

des voyous
la source
1
Pourquoi avez-vous dévalorisé cela? Expliquez-moi ou c'est inutile ...
rools
Les haineux (downvoters anonymes silencieux) détesteront. J'ai appris quelque chose de votre réponse, alors voici une upvote. :-)
Mac