web-dev-qa-db-fra.com

Quelle est la différence entre "#! / Usr / bin / env bash" et "#! / Usr / bin / bash"?

Dans l'en-tête d'un script bash, quelle est la différence entre ces deux instructions?

  1. #!/usr/bin/env bash

  2. #!/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?

304
tarrsalah

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.

276
Alec Bennett

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.

50
Dolphiniac

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

11
safay

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.

3
Mecki

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

3
sudo97