Existe-t-il un moyen de choisir dynamiquement l'interpréteur qui exécute un script? J'ai un script que j'exécute sur deux systèmes différents, et l'interpréteur que je veux utiliser se trouve à différents endroits sur les deux systèmes. Ce que je dois finir par changer, c'est la ligne de hachage à chaque fois que je passe. Je voudrais faire quelque chose qui est l' équivalent logique de cela (je me rends compte que cette construction exacte est impossible):
if running on system A:
#!/path/to/python/on/systemA
elif running on system B:
#!/path/on/systemB
#Rest of script goes here
Ou encore mieux serait ceci, pour qu'il essaie d'utiliser le premier interprète, et s'il ne le trouve pas, il utilise le second:
try:
#!/path/to/python/on/systemA
except:
#!path/on/systemB
#Rest of script goes here
Évidemment, je peux plutôt l'exécuter comme
/path/to/python/on/systemA myscript.py
ou
/path/on/systemB myscript.py
selon l'endroit où je suis, mais j'ai en fait un script wrapper qui se lance myscript.py
, donc je voudrais spécifier le chemin vers l'interpréteur python par programme plutôt qu'à la main.
if
condition n'est pas une option pour vous? comme,if something; then /bin/sh restofscript.sh elif...
Réponses:
Non, ça ne marchera pas. Les deux caractères
#!
doivent absolument être les deux premiers caractères du fichier (comment spécifieriez-vous de toute façon ce qui a interprété l'instruction if?). Ceci constitue le "nombre magique" que laexec()
famille de fonctions détecte lorsqu'elles déterminent si un fichier qu'elles sont sur le point d'exécuter est un script (qui a besoin d'un interprète) ou un fichier binaire (qui ne le fait pas).Le format de la ligne shebang est assez strict. Il doit avoir un chemin absolu vers un interprète et tout au plus un argument.
Ce que vous pouvez faire, c'est utiliser
env
:Maintenant, le chemin vers
env
est généralement/usr/bin/env
, mais techniquement, ce n'est pas une garantie.Cela vous permet d'ajuster la
PATH
variable d'environnement sur chaque système afininterpreter
(que ce soitbash
,python
ou ,perl
ou tout ce que vous avez) est trouvé.Un inconvénient de cette approche est qu'il sera impossible de transmettre un argument de manière portable à l'interprète.
Cela signifie que
et
ne fonctionnera probablement pas sur certains systèmes.
Une autre approche évidente consiste à utiliser les autotools GNU (ou un système de modélisation plus simple) pour trouver l'interpréteur et placer le chemin correct dans le fichier en une
./configure
étape, qui serait exécutée lors de l'installation du script sur chaque système.On pourrait également recourir à l'exécution du script avec un interpréteur explicite, mais c'est évidemment ce que vous essayez d'éviter:
la source
#!
doit venir au début, car ce n'est pas le shell qui traite cette ligne. Je me demandais s'il y avait un moyen de mettre de la logique à l' intérieur de la ligne de hachage qui serait équivalent à l'if / else. J'espérais également éviter de déconner avec monPATH
mais je suppose que ce sont mes seules options.#!/usr/bin/awk
, vous pouvez fournir exactement un argument, comme#!/usr/bin/awk -f
. Si le binaire que vous pointez estenv
, l'argument est le binaire que vous demandezenv
, comme dans#!/usr/bin/env awk
./usr/bin/env
avec le seul argumentawk -f
.foo.awk
avec la ligne hashbang#!/usr/bin/env awk -f
et appelez-le avec./foo.awk
alors, sur Linux, ce quienv
voit , ce sont les deux paramètresawk -f
et./foo.awk
. Il va en fait chercher/usr/bin/awk -f
(etc.) avec un espace.Vous pouvez toujours créer un script wrapper pour trouver l'interpréteur correct pour le programme réel:
Enregistrez le wrapper dans les utilisateurs
PATH
sousprogram
et mettez le programme réel de côté ou sous un autre nom.J'ai utilisé
#!/bin/bash
dans le hashbang à cause duflags
tableau. Si vous n'avez pas besoin de stocker un nombre variable d'indicateurs ou autre et que vous pouvez vous en passer, le script devrait fonctionner de manière portable avec#!/bin/sh
.la source
exec "$interpreter" "${flags[@]}" "$script" "$@"
aussi utilisé pour garder l'arbre de processus plus propre. Il propage également le code de sortie.exec
.#!/bin/sh
mieux au lieu de#!/bin/bash
? Même s'il/bin/sh
s'agit d'un lien symbolique vers un autre shell, il devrait exister sur la plupart (sinon tous) les systèmes * nix, et il forcerait l'auteur du script à créer un script portable plutôt que de tomber dans des bashismes.flags
est une fonctionnalité non standard, mais il est suffisamment utile pour stocker un nombre variable d'indicateurs, j'ai donc décidé de le conserver.script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@"
. Vous pouvez également ajouter une vérification d'erreur pour les échecs d'exécution.Vous pouvez également écrire un polyglotte (combiner deux langues). / bin / sh est garanti d'exister.
Cela a l'inconvénient d'un code laid et peut-être que certains
/bin/sh
s pourraient être confus. Mais il peut être utilisé lorsqu'ilenv
n'existe pas ou existe ailleurs que / usr / bin / env. Il peut également être utilisé si vous souhaitez faire une sélection assez sophistiquée.La première partie du script détermine l'interpréteur à utiliser lorsqu'il est exécuté avec / bin / sh comme interprète, mais est ignoré lorsqu'il est exécuté par l'interpréteur approprié. Utilisez
exec
pour empêcher le shell de fonctionner plus que la première partie.Exemple Python:
la source
exec "$interpreter" "$0" "$@"
le nom du script à l'interprète lui-même. (Et puis j'espère que personne n'a menti lors de l'installation$0
.)#!
, Scala ignore tout jusqu'à une correspondance!#
; cela vous permet de mettre du code de script arbitrairement complexe dans un langage arbitraire, puisexec
le moteur d'exécution Scala avec le script.Je préfère les réponses de Kusalananda et d'ilkkachu, mais voici une réponse alternative qui fait plus directement ce que la question demandait, tout simplement parce qu'elle a été posée.
Notez que vous ne pouvez le faire que lorsque l'interpréteur autorise l'écriture de code dans le premier argument. Ici,
-e
et tout ce qui suit est pris mot pour mot comme un argument pour ruby. Pour autant que je sache, vous ne pouvez pas utiliser bash pour le code shebang, carbash -c
nécessite que le code soit dans un argument séparé.J'ai essayé de faire la même chose avec python pour le code shebang:
mais cela s'avère trop long et linux (au moins sur ma machine) tronque le shebang à 127 caractères. Veuillez excuser l'utilisation de
exec
pour insérer des sauts de ligne car python ne permet pas les essais-exceptions ou lesimport
s sans sauts de ligne.Je ne sais pas à quel point c'est portable, et je ne le ferais pas avec du code destiné à être distribué. Néanmoins, c'est faisable. Peut-être que quelqu'un le trouvera utile pour un débogage rapide et sale ou quelque chose.
la source
Bien que cela ne sélectionne pas l'interpréteur dans le script shell (il le sélectionne par machine), c'est une alternative plus facile si vous avez un accès administratif à toutes les machines sur lesquelles vous essayez d'exécuter le script.
Créez un lien symbolique (ou un lien dur si vous le souhaitez) pour pointer vers le chemin d'interpréteur souhaité. Par exemple, sur mon système, perl et python sont dans / usr / bin:
créerait un lien symbolique pour permettre au hashbang de se résoudre pour / bin / perl, etc. Cela préserve également la possibilité de passer des paramètres aux scripts.
la source
J'ai été confronté à un problème similaire comme celui-ci aujourd'hui (
python3
pointant vers une version de python qui était trop ancienne sur un système), et j'ai trouvé une approche qui est un peu différente de celles discutées ici: Utilisez la "mauvaise" version de python pour démarrer dans le "bon". La limitation est qu'une certaine version de python doit être accessible de manière fiable, mais cela peut généralement être réalisé par exemple#!/usr/bin/env python3
.Donc ce que je fais, c'est commencer mon script avec:
Ce que cela signifie:
la source