Dans l'en-tête d'un script bash
, quelle est la différence entre ces deux instructions?
#!/usr/bin/env bash
#!/usr/bin/bash
Lorsque j'ai essayé de voir la page de manuel env
, j'ai la définition suivante:
env - run a program in a modified environment
Qu'est-ce que ça veut dire?
L'exécution d'une commande via /usr/bin/env
présente l'avantage de rechercher la version par défaut du programme dans votre version actuelle env .
De cette façon, vous n'avez pas à le rechercher à un emplacement spécifique du système, car ces chemins peuvent se trouver à des emplacements différents sur des systèmes différents. Tant qu'il est sur votre chemin, il le trouvera.
Un inconvénient est que vous ne pourrez pas passer plus d’un argument (par exemple, vous ne pourrez pas écrire /usr/bin/env awk -f
) si vous voulez supporter Linux, car POSIX est vague sur la façon dont la ligne doit être interprété et Linux interprète tout après le premier espace pour désigner un seul argument. Vous pouvez utiliser /usr/bin/env -S
sur certaines versions de env
pour résoudre ce problème, mais le script deviendra encore moins portable et se brisera sur des systèmes relativement récents (par exemple, même Ubuntu 16.04 sinon plus tard).
Un autre inconvénient est que, puisque vous n'appelez pas d'exécutable explicite, vous risquez des erreurs et des problèmes de sécurité des systèmes multi-utilisateurs (si quelqu'un réussissait à obtenir son exécutable nommé bash
dans votre chemin, par exemple).
#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash #gives you explicit control on a given system of what executable is called
Dans certaines situations, la première solution peut être préférée (comme l'exécution de python scripts avec plusieurs versions de python, sans avoir à retravailler la ligne de l'exécutable). Mais dans les situations où la sécurité est au centre des préoccupations, ce dernier serait préféré car il limite les possibilités d’injection de code.
Utiliser #!/usr/bin/env NAME
permet au shell de rechercher la première correspondance de NAME dans la variable d’environnement $ PATH. Cela peut être utile si vous ne connaissez pas le chemin absolu ou si vous ne voulez pas le rechercher.
Au lieu de définir explicitement le chemin d'accès à l'interpréteur comme dans /usr/bin/bash/
, à l'aide de la commande env, l'interpréteur est recherché et lancé à partir de son emplacement d'origine. Cela a les deux avantages et inconvénients
Si les scripts Shell commencent par _#!/bin/bash
_, ils seront toujours exécutés avec bash
à partir de _/bin
_. S'ils commencent toutefois par _#!/usr/bin/env bash
_, ils rechercheront bash
dans _$PATH
_ et commenceront par le premier qu'ils pourront trouver.
Pourquoi cela serait-il utile? Supposons que vous souhaitiez exécuter des scripts bash
, qui nécessitent bash 4.x ou plus récent, mais que votre système ne dispose que de bash
3.x installé et que votre distribution ne propose actuellement pas de version plus récente ou que vous ne l'êtes pas. administrateur et ne peut pas modifier ce qui est installé sur ce système.
Bien sûr, vous pouvez télécharger le code source bash et construire votre propre bash à partir de zéro, en le plaçant par exemple sur _~/bin
_. Et vous pouvez également modifier votre variable _$PATH
_ dans votre fichier _.bash_profile
_ pour inclure _~/bin
_ en tant que première entrée (_PATH=$HOME/bin:$PATH
_ en tant que _~
_ ne sera pas développé en _$PATH
_). Si vous appelez maintenant bash
, le shell le recherchera d'abord dans _$PATH
_ dans l'ordre, de sorte qu'il commence par _~/bin
_, où il trouvera votre bash
. La même chose se produit si les scripts recherchent bash
à l'aide de _#!/usr/bin/env bash
_, de sorte qu'ils fonctionneraient désormais sur votre système avec votre build personnalisé bash
.
Un inconvénient est que cela peut conduire à un comportement inattendu, par exemple. le même script sur la même machine peut être exécuté avec des interpréteurs différents pour des environnements différents ou des utilisateurs avec des chemins de recherche différents, ce qui provoque toutes sortes de maux de tête.
Le plus gros inconvénient de env
est que certains systèmes n'autorisent qu'un seul argument. Vous ne pouvez donc pas le faire _#!/usr/bin/env <interpreter> <arg>
_, car ils verront _<interpreter> <arg>
_ comme un argument (ils le traiteront comme si l'expression a été citée) et ainsi env
recherchera un interprète nommé _<interpreter> <arg>
_. Notez que ce n'est pas un problème de la commande env
elle-même, qui a toujours permis de transmettre plusieurs paramètres, mais avec l'analyseur Shebang du système qui analyse cette ligne avant même d'appeler env
. En attendant, cela a été corrigé sur la plupart des systèmes, mais si votre script veut être ultra portable, vous ne pouvez pas compter sur le fait que cela a été corrigé sur le système que vous allez exécuter.
Cela peut même avoir des conséquences sur la sécurité, par exemple si Sudo
n'a pas été configuré pour nettoyer l'environnement ou _$PATH
_ a été exclu du nettoyage. Laissez-moi démontrer ceci:
Généralement, _/bin
_ est un endroit bien protégé, seul root
peut y changer quoi que ce soit. Votre répertoire personnel n’est pas, cependant, tout programme que vous exécutez est en mesure de le modifier. Cela signifie qu'un code malveillant pourrait placer un faux bash
dans un répertoire caché, modifier votre _.bash_profile
_ pour l'inclure dans votre _$PATH
_, de sorte que tous les scripts utilisant _#!/usr/bin/env bash
_ seront exécutés. avec ce faux bash
. Si Sudo
conserve _$PATH
_, vous avez de gros problèmes.
Par exemple. Considérer qu'un outil crée un fichier _~/.evil/bash
_ avec le contenu suivant:
_#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "$@"
_
Faisons un script simple _sample.sh
_:
_#!/usr/bin/env bash
echo "Hello World"
_
Preuve de concept (sur un système où Sudo
garde _$PATH
_):
_$ ./sample.sh
Hello World
$ Sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ Sudo ./sample.sh
All your base are belong to us...
Hello World
_
Habituellement, les coquilles classiques doivent toutes être situées dans _/bin
_ et si vous ne voulez pas les y placer pour une raison quelconque, ce n'est pas un problème de placer un lien symbolique dans _/bin
_ qui pointe vers leurs emplacements réels. (ou peut-être _/bin
_ est lui-même un lien symbolique), donc j'irais toujours avec _#!/bin/sh
_ et _#!/bin/bash
_. Il y en aurait trop qui casseraient si cela ne fonctionnait plus. Ce n’est pas que POSIX aurait besoin de ces postes (POSIX ne standardise pas les noms de chemins et ne standardise donc même pas la fonction Shebang), mais ils sont si courants que même si un système ne propose pas un _/bin/sh
_, il comprendrait probablement toujours _#!/bin/sh
_ et saurait quoi faire avec. Peut-être n’est-ce que pour la compatibilité avec le code existant.
Mais pour les interprètes optionnels plus modernes et non standard tels que Perl, PHP, Python ou Ruby, ce n'est pas vraiment spécifié où ils devraient être situés. Ils peuvent être dans _/usr/bin
_ mais ils peuvent également l'être dans _/usr/local/bin
_ ou dans une branche de hiérarchie complètement différente (_/opt/...
_, _/Applications/...
_, etc.). C'est pourquoi ils utilisent souvent la syntaxe _#!/usr/bin/env xxx
_ Shebang.
Je trouve cela utile, parce que quand je ne connaissais pas env, avant de commencer à écrire le script, je faisais ceci:
type nodejs > scriptname.js #or any other environment
et puis je modifiais cette ligne dans le fichier en Shebang.
Je le faisais, parce que je ne me souvenais pas toujours où se trouvait nodejs sur mon ordinateur -/usr/bin/ou/bin /, donc pour moi env
est très utile. Peut-être y at-il des détails à ce sujet, mais c’est ma raison