Choisir un interprète après le début du script par exemple if / else dans hashbang

Existe-t-il un moyen de choisir dynamicment l'interpréteur qui exécute un script? J'ai un script que j'utilise sur deux systèmes différents et l'interpréteur que je veux utiliser est situé à différents endroits sur les deux systèmes. Ce que je finis par devoir est de changer la ligne hashbang chaque fois que je bascule. Je voudrais faire quelque chose qui est l'équivalent logique de ceci (je réalise 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 cela, de sorte qu'il essaie d'utiliser le premier interprète, et s'il ne le trouve pas utilise le second:

 try: #!/path/to/python/on/systemA except: #!path/on/systemB #Rest of script goes here 

Evidemment, je peux l'exécuter sous /path/to/python/on/systemA myscript.py ou /path/on/systemB myscript.py fonction de l'endroit où je suis, mais j'ai en fait un script wrapper qui lance myscript.py , Je voudrais donc spécifier le path de l'interpréteur python par programme plutôt que par la main.

Non, ça ne marchera pas. Les deux personnages #! doit absolument être les deux premiers caractères du file (comment spécifieriez-vous ce qui a interprété l'instruction if de toute façon?). Cela constitue le «nombre magique» que la famille de fonctions exec() détecte lorsqu'il détermine si un file qu'il est sur le point d'exécuter est un script (qui a besoin d'un interpréteur) ou un file binary.

Le format de la ligne de shebang est assez ssortingct. Il doit avoir un path absolu vers un interpréteur et au plus un argument.

Ce que vous pouvez faire est d'utiliser env :

 #!/usr/bin/env interpreter 

Maintenant, le path vers env est habituellement /usr/bin/env , mais techniquement ce n'est pas une garantie.

Cela vous permet d'ajuster la variable d'environnement PATH sur chaque système afin que l' interpreter (que ce soit bash , python ou perl ou tout ce que vous avez) soit trouvé.

Un inconvénient de cette approche est qu'il sera impossible de transmettre un argument à l'interprète.

Ceci veut dire cela

 #!/usr/bin/env awk -f 

et

 #!/usr/bin/env sed -f 

est peu probable de travailler sur certains systèmes.

Une autre approche évidente consiste à utiliser les autotools GNU (ou un système de templates plus simple) pour find l'interpréteur et placer le path correct dans le file dans une étape ./configure , qui serait exécutée lors de l'installation du script sur chaque système.

On pourrait aussi utiliser le script avec un interpréteur explicite, mais c'est évidemment ce que vous essayez d'éviter:

 $ sed -f script.sed 

Vous pouvez toujours créer un script wrapper pour find l'interprète correct pour le programme réel:

 #!/bin/bash if something ; then interpreter=this script=/some/path/to/program.real flags=() else interpreter=that script=/other/path/to/program.real flags=(-x -y) fi exec "$interpreter" "${flags[@]}" "$script" "$@" 

Sauvegardez l'encapsuleur dans le PATH l'user comme program et mettez le programme actuel de côté ou avec un autre nom.

J'ai utilisé #!/bin/bash dans le hashbang à cause du tableau des flags . Si vous n'avez pas besoin de stocker un nombre variable de drapeaux ou si vous ne pouvez pas le faire, le script doit fonctionner de manière portable avec #!/bin/sh .

Vous pouvez également écrire un polyglot (combiner deux langues). / bin / sh est garanti exister.

Cela a l'inconvénient du code laide et peut-être que certains /bin/sh s pourraient potentiellement devenir confus. Mais il peut être utilisé quand env n'existe pas ou existe ailleurs que / usr / bin / env. Il peut également être utilisé si vous voulez faire une sélection assez chic.

La première partie du script détermine l'interpréteur à utiliser lors de l'exécution avec / bin / sh en tant qu'interprète, mais est ignorée lorsqu'elle est exécutée par l'interpréteur correct. Utilisez exec pour empêcher le shell de s'exécuter plus que la première partie.

Exemple Python:

 #!/bin/sh ''' ' 2>/dev/null # Python thinks this is a ssortingng, docssortingng unfortunately. # The shell has just sortinged running the <newline> program. find_best_python () { for candidate in pypy3 pypy python3 python; do if [ -n "$(which $candidate)" ]; then echo $candidate return fi done echo "Can't find any Python" >/dev/stderr exit 1 } interpreter="$(find_best_python)" # Replace with something fancier. # Run the rest of the script exec "$interpreter" "$0" "$@" ''' 

Je préfère les réponses de Kusalananda et d'ilkkachu, mais voici une autre réponse qui répond plus directement à la question, simplement parce qu'on l'a posée.

 #!/usr/bin/ruby -e exec "non-existing-interpreter", ARGV[0] rescue exec "python", ARGV[0] if True: print("hello world!") 

Notez que vous ne pouvez le faire que lorsque l'interpréteur permet d'écrire du code dans le premier argument. Ici, -e et tout ce qui suit est pris verbatim en tant que 1 argument pour ruby. Pour autant que je sache, vous ne pouvez pas utiliser bash pour le code shebang, car bash -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:

 #!/usr/bin/python -cexec("import sys,os\ntry: os.execlp('non-existing-interpreter', 'non-existing-interpreter', sys.argv[1])\nexcept: os.execlp('ruby', 'ruby', sys.argv[1])") if true puts "hello world!" end 

mais il s'avère trop long et linux (au less sur ma machine) tronque le shebang à 127 caractères. Veuillez excuser l'utilisation de exec pour insert des newlines car python n'autorise pas try-excepts ou import s sans newlines.

Je ne sais pas comment c'est portable, et je ne le ferais pas sur le code destiné à être dissortingbué. Néanless, c'est faisable. Peut-être que quelqu'un le finda utile pour le debugging rapide et sale ou quelque chose comme ça.

Bien que cela ne sélectionne pas l'interpréteur dans le script shell (il le sélectionne par machine), il s'agit d'une alternative plus facile si vous avez un access administratif à toutes les machines sur lesquelles vous essayez d'exécuter le script.

Créez un lien symbolique (ou un lien fixe si vous le souhaitez) pour pointer vers le path d'interprétation souhaité. Par exemple, sur mon système, perl et python sont dans / usr / bin:

 cd /bin ln -s /usr/bin/perl perl ln -s /usr/bin/python python 

créerait un lien symbolique pour permettre au hashbang de résoudre pour / bin / perl, etc. Cela préserve la capacité de transmettre des parameters aux scripts.