Si les scripts shell commencent par #!/bin/bash
, ils fonctionneront toujours avec bash
à partir de /bin
. S'ils commencent cependant par #!/usr/bin/env bash
, ils rechercheront bash
en $PATH
puis commencer par le premier qu'ils peuvent trouver.
Pourquoi cela serait-il utile ? Supposons que vous souhaitiez exécuter bash
scripts, qui nécessitent bash 4.x ou plus récent, mais votre système n'a que bash
3.x est installé et actuellement votre distribution n'offre pas de version plus récente ou vous n'êtes pas administrateur et ne pouvez pas modifier ce qui est installé sur ce système.
Bien sûr, vous pouvez télécharger le code source de bash et créer votre propre bash à partir de zéro, en le plaçant sur ~/bin
par exemple. Et vous pouvez aussi modifier votre $PATH
variable dans votre .bash_profile
fichier pour inclure ~/bin
comme première entrée (PATH=$HOME/bin:$PATH
comme ~
ne se développera pas en $PATH
). Si vous appelez maintenant le bash
, le shell le cherchera d'abord dans $PATH
dans l'ordre, il commence donc par ~/bin
, où il trouvera votre bash
. La même chose se produit si les scripts recherchent bash
en utilisant #!/usr/bin/env bash
, donc ces scripts fonctionneraient maintenant sur votre système en utilisant votre bash
personnalisé construire.
L'inconvénient est que cela peut entraîner un comportement inattendu, par ex. le même script sur la même machine peut s'exécuter avec différents interpréteurs pour différents environnements ou utilisateurs avec différents chemins de recherche, provoquant toutes sortes de maux de tête.
Le plus gros inconvénient avec env
est que certains systèmes n'autorisent qu'un seul argument, vous ne pouvez donc pas faire cela #!/usr/bin/env <interpreter> <arg>
, car les systèmes verront <interpreter> <arg>
comme un argument (ils le traiteront comme si l'expression était entre guillemets) et donc env
recherchera un interpréteur nommé <interpreter> <arg>
. Notez que ce n'est pas un problème du env
commande elle-même, qui permettait toujours de passer plusieurs paramètres mais avec l'analyseur shebang du système qui analyse cette ligne avant même d'appeler env
. Entre-temps, 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 utiliserez.
Cela peut même avoir des implications sur la sécurité, par ex. si sudo
n'a pas été configuré pour nettoyer l'environnement ou $PATH
a été exclu du nettoyage. Laissez-moi vous montrer ceci :
Généralement /bin
est un endroit bien protégé, seulement root
est capable d'y changer quoi que ce soit. Votre répertoire personnel n'est pas, cependant, n'importe quel programme que vous exécutez est capable d'y apporter des modifications. Cela signifie qu'un code malveillant pourrait placer un faux bash
dans un répertoire caché, modifiez votre .bash_profile
pour inclure ce répertoire dans votre $PATH
, donc tous les scripts utilisant #!/usr/bin/env bash
finira par fonctionner avec ce faux bash
. Si sudo
conserve $PATH
, vous avez de gros problèmes.
Par exemple. considérez 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 "[email protected]"
Faisons un script simple sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Preuve de concept (sur un système où sudo
conserve $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 shells classiques doivent tous être situés dans /bin
et si vous ne voulez pas les placer là pour une raison quelconque, ce n'est vraiment pas un problème de placer un lien symbolique dans /bin
qui pointe vers leurs emplacements réels (ou peut-être /bin
lui-même est un lien symbolique), donc j'irais toujours avec #!/bin/sh
et #!/bin/bash
. Il y a tout simplement trop de choses qui se briseraient si elles ne fonctionnaient plus. Ce n'est pas que POSIX nécessiterait ces positions (POSIX ne standardise pas les noms de chemin et donc il ne standardise même pas du tout la fonctionnalité shebang) mais elles sont si courantes que même si un système n'offre pas un /bin/sh
, il comprendrait probablement encore #!/bin/sh
et savoir quoi en faire et peut-être uniquement pour la compatibilité avec le code existant.
Mais pour les interpréteurs facultatifs plus modernes, non standard, tels que Perl, PHP, Python ou Ruby, il n'est pas vraiment spécifié où ils doivent être situés. Ils peuvent être en /usr/bin
mais ils peuvent aussi être en /usr/local/bin
ou dans une toute autre branche hiérarchique (/opt/...
, /Applications/...
, etc.). C'est pourquoi ceux-ci utilisent souvent le #!/usr/bin/env xxx
syntaxe shebang.
Utilisation de #!/usr/bin/env NAME
oblige le shell à 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.
Exécuter une commande via /usr/bin/env
a l'avantage de rechercher la version par défaut du programme dans votre env actuel ferraille.
De cette façon, vous n'avez pas à le rechercher à un endroit spécifique du système, car ces chemins peuvent se trouver à différents endroits sur différents systèmes. 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 souhaitez prendre en charge Linux, car POSIX est vague sur la façon dont la ligne doit être interprétée, 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 contourner cela, mais le script deviendra alors encore moins portable et cassera sur des systèmes assez récents (par exemple, même Ubuntu 16.04 si ce n'est pas plus tard).
Un autre inconvénient est que puisque vous n'appelez pas un exécutable explicite, il peut y avoir des erreurs et des problèmes de sécurité des systèmes multi-utilisateurs (si quelqu'un réussit à obtenir son exécutable appelé bash
sur 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, le premier peut être préféré (comme exécuter des scripts python avec plusieurs versions de python, sans avoir à retravailler la ligne exécutable). Mais dans les situations où la sécurité est au centre des préoccupations, cette dernière serait préférée, car elle limite les possibilités d'injection de code.