Existe-t-il une commande permettant d'exécuter un script en fonction de sa ligne shebang?

46

Si je veux exécuter un script bash dont les autorisations d'exécution ne sont pas définies, je peux faire:

bash script.sh

Que dois-je utiliser à la place de bashsi le script n'est pas exécutable et que je ne connais pas l'interprète correct? Existe-t-il une commande qui recherche l'interprète de la ligne shebang et exécute le script avec?

Aivar
la source
Quel est le problème avec bash?
Steffen
@steffen comment savez-vous que le fichier en question est un script bash?
Muru
@muru citation de Question: "Si je veux exécuter un script bash ..." De plus (même s'il ne s'agit pas d'un script bash), si cela bash whateverfonctionne, pourquoi utiliser quelque chose de différent? bash est disponible sur pratiquement tous les systèmes * ix, alors pourquoi s'embêter ...
steffen
1
@steffen avez-vous lu le reste de la question? Ils disent: "Si ... alors je peux faire: ..." et "Que devrais-je utiliser à la place de bash si ... je ne connais pas le bon interprète ?"
Muru
@muru: peut-être que je ne vois pas l'évidence. Mais si le fichier / a / une ligne shebang, comme indiqué dans la question, bash fera exactement ce qui est demandé. Comme sera perl, selon la réponse ci-dessous. Alors, quel est l'avantage de ne pas utiliser bash?
Steffen

Réponses:

67

Oui. Cela s'appelle perl:

perl foo.bash    # works
perl foo.lua     # works
perl foo.clisp   # works
perl foo.csh     # works
perl foo.php     # works
perl foo.gnuplot # works (no arguments)
perl foo.pl      # works (obviously)
perl foo.py      # works
perl foo.sh      # works
perl foo.tcl     # works
perl foo.rb      # works
perl foo.nodejs  # works
perl foo.r       # works
perl foo.oct     # works
perl foo.csharp  # works (no arguments)

Ceci est mentionné dans la documentation de Perl :

Si la #!ligne ne contient pas le mot "perl" ni le mot "indir", le programme nommé d'après le #!est exécuté à la place de l'interpréteur Perl. C'est un peu bizarre, mais cela aide les utilisateurs de machines qui ne le font pas #!, car ils peuvent dire à un programme que leur shell est / usr / bin / perl, et Perl enverra ensuite le programme à l'interprète approprié.

Ole Tange
la source
40
Eh bien, c'est juste sale.
slim
1
Est-ce que l'extension de fichier .jsfonctionne aussi?
Pysis
1
Aussi, quelles machines ne font pas #!. J'ai l'air de quelques-uns à plus maintenant, et n'ai pas connu ce problème.
Pysis
1
Ok, c'est juste que votre exemple semblait mettre en évidence de nombreuses extensions de fichiers, plutôt que de montrer plusieurs lignes de shebang à partir de fichiers, et je suppose que j'ai supposé que c'était ainsi que fonctionnait sa prédiction.
Pysis
2
J'aime la façon man perlrunpudique d'admettre que c'est "légèrement bizarre" :). Je pense que cela devrait être traité comme une curiosité destinée aux environnements non-UNIX et aux très très anciennes versions d'UNIX.
slim
25

Les scripts n'ont pas nécessairement un shebang

Si le script a été exécuté à partir de l'interprète, vous ne pouvez pas être sûr qu'il a le shebang du tout . Les scripts exécutés à partir de l'interpréteur n'ont pas besoin du shebang , si vous appelez l'interpréteur pour exécuter le code.

La réponse est donc non, aucune commande ne permet de savoir avec certitude quelle est la langue (interprète) avec laquelle exécuter le script. Vous pouvez cependant toujours regarder à l'intérieur du script et voir s'il a le shebang à découvrir.

Les règles en bref:

  1. Lorsque vous exécutez le script, l'appel de l'interprète annule toujours les shebangs possibles, exécutables ou non, shebang ou non.
  2. S'il n'est pas exécutable et exécuté à partir de l'interprète, le script n'a pas besoin de shebang.
  3. Si le script est exécuté sans appeler d'abord l'interprète, il a besoin (et utilise) le shebang pour savoir quel interprète appeler, et il doit être exécutable pour avoir la "permission" d'appeler l'interprète à partir de son shebang.

Si le script n'a pas de shebang, il n'y a aucune information (directe *) à l'intérieur du script pour indiquer quel interpréteur utiliser.

Ayant dit cela

Vous pouvez bien sûr toujours écrire un script de wrapper pour essayer de savoir si le script a le shebang et en lire l'interprète, puis l'exécuter à partir de l'interprète trouvé.

Un exemple

#!/usr/bin/env python3
import subprocess
import sys

args = sys.argv[1:]; script = args[0]

try:
    lang = open(script).readlines()[0].replace("#!", "").strip().split()[-1]
    cmd = [lang, script]+args[1:]
    subprocess.call(cmd)
except (PermissionError, FileNotFoundError, IndexError):
    print("No valid shebang found")
  • Enregistrez-le tryrunen tant que dans $PATH(par exemple ~/bin, créez le répertoire s'il n'existe pas, déconnectez-vous puis reconnectez-vous), rendez-le exécutable . Puis en cours d'exécution:

    tryrun /path/to/nonexecutablescript

    appelle (testé) l'interprète correct sur mon non-exécutable pythonet mes bashscripts.

Explication

  • Le script lit simplement la première ligne du script, supprime le #!et utilise le reste pour appeler l'interprète.
  • S'il ne parvient pas à appeler un interprète valide, il déclenchera soit un, PermissionErrorsoit un FileNotFoundError.

Remarque

L'extension ( .sh, .pyetc.) ne joue aucun rôle dans la détermination de l'interpréteur approprié sous Linux.


(* Il est bien sûr possible de développer un algorithme "intelligent" de devin pour déterminer la syntaxe à partir du code.)

Jacob Vlijm
la source
OK, cela signifie que bien que Linux ait l'extraction shebang implémentée quelque part (afin de pouvoir sélectionner l'interpréteur correct pour les srcipts exécutables), elle n'est pas fournie en tant que commande standard autonome.
Aivar
@Aivar extraire le shebang n'est pas la question, mais exécuter du code sans elle est parfaitement possible.
Jacob Vlijm
@Aivar Ah, je vois ce que vous voulez dire. Si le script est exécutable et exécuté sans langue dans la commande, le script appelle l'interpréteur, et non l'inverse.
Jacob Vlijm
1
@JacobVlijm Je ne dirais pas que "le script appelle l'interprète", plutôt "le noyau Linux prend la ligne shebang pour déterminer quel interprète appeler lors de l'exécution du script".
Paŭlo Ebermann
@ PaŭloEbermann Merci! Vrai bien sur. Le noyau prend en charge toute la procédure dans les deux sens, mais au sens figuré, et je pense mieux pour la compréhension, c'est dire que le script est "autorisé" à prendre en charge quel interprète appeler (et le faire réellement). Pas sûr de la formulation, mais je voudrais la décrire comme si l'initiative était sur le script, alors que le noyau fait le travail.
Jacob Vlijm
6

Vous pouvez y parvenir avec un script comme celui-ci:

#!/bin/bash

copy=/tmp/runner.$$
cp $1 ${copy}
chmod u+x ${copy}
${copy}
rm ${copy}

Ainsi:

$ echo "echo hello" > myscript
$ ./myscript
bash: ./myscript: Permission denied
$ ./runscript myscript 
hello

Je recommande de ne pas faire cela. Les autorisations sont là pour une raison. Ceci est un programme pour subvertir les autorisations.

Notez que la gestion de shebang est une fonction du noyau (dans le code source Linux - fs/binfmt_script.c). Fondamentalement, le processus invoquant directement un script ne connaît pas le #!- le noyau l'utilise pour déterminer s'il doit lancer un interpréteur.

svelte
la source
2
J'avais toujours supposé que c'était une fonction du shell, PAS du noyau. Appris quelque chose de nouveau aujourd'hui.
boatcoder
1
@boatcoder - Puisque cela vous intéresse, a ajouté un lien vers l'endroit où le code source Linux le gère.
slim