470 votes

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

Dans l'en-tête d'un script de Bash, quelle est la différence entre ces deux déclarations :

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Lorsque j'ai consulté le env page de manuel j'obtiens cette définition :

 env - run a program in a modified environment

Qu'est-ce que cela signifie ?

11 votes

6 votes

Qui peut me dire pourquoi cette question est fermée, alors que "lié à la programmation ou au développement de logiciels" ne l'est pas ?

1 votes

Je suis d'accord pour dire qu'il ne s'agit pas d'un hors-sujet, mais il s'agit probablement d'un doublon de plusieurs autres questions telles que celui-ci .

394voto

Alec Bennett Points 501

Exécution d'une commande par /usr/bin/env a l'avantage de rechercher la version par défaut du programme dans votre système actuel. env. l'environnement.

Ainsi, vous n'avez pas à le chercher à un endroit précis du système, car ces chemins peuvent se trouver à différents endroits sur différents systèmes. Tant qu'il se trouve dans 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, comme POSIX est vague sur la façon dont la ligne doit être interprétée, et Linux interprète tout ce qui suit le premier espace pour indiquer un seul argument. Vous pouvez utiliser /usr/bin/env -S sur certaines versions de env pour contourner cela, mais alors le script deviendra encore moins portable et se cassera sur des systèmes assez récents (par exemple même Ubuntu 16.04 si ce n'est plus).

Un autre inconvénient est que, puisque vous n'appelez pas un exécutable explicite, il y a un potentiel d'erreurs, et sur les systèmes multi-utilisateurs, des problèmes de sécurité (si quelqu'un réussissait à faire appeler son exécutable 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 peut être préférée (comme l'exécution de 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, la seconde serait préférable, car elle limite les possibilités d'injection de code.

26 votes

Un autre inconvénient est que vous ne pouvez pas passer un argument supplémentaire à l'interprète.

2 votes

@KeithThompson : Info incorrecte . Vous pouvez passer des options à l'interpréteur sous-jacent en utilisant /usr/bin/env !

5 votes

@GauravAgarwal : Pas sur mon système. Un script contenant juste cette unique ligne : #!/usr/bin/env echo Hello se plaint : /usr/bin/env: echo Hello: No such file or directory . Apparemment, il traite echo Hello comme un seul argument à /usr/bin/env .

82voto

Dolphiniac Points 459

Utilisation de #!/usr/bin/env NAME fait rechercher par l'interpréteur de commandes la première correspondance de NOM 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.

13 votes

Au moins, vous devez savoir où se trouve env :).

2 votes

Excellente réponse. Elle explique succinctement ce que fait l'environnement, au lieu de dire "choisit le programme en fonction de la configuration de votre système".

0 votes

Voici la situation que j'ai rencontrée. Lorsque je lance la commande "./script.sh" avec "#!/usr/bin/env bash" dans la première ligne de script.sh, il donne un retour de "-bash : ./tools/dist_train.sh : Permission refusée". Lorsque je lance la commande "bash ./script.sh". Cela fonctionne bien. Pourriez-vous me dire pourquoi "#!/usr/bin/env NOM" ne fonctionne pas... ?

21voto

Mecki Points 35351

Si les scripts shell commencent par #!/bin/bash ils fonctionneront toujours avec bash de /bin . Si toutefois ils commencent par #!/usr/bin/env bash ils chercheront bash sur $PATH et commencer avec le premier qu'ils peuvent trouver.

Pourquoi cela serait-il utile ? Supposons que vous vouliez exécuter bash qui requièrent bash 4.x ou plus récent, alors que votre système n'a que bash 3.x installé et actuellement votre distribution ne propose pas de version plus récente ou vous n'êtes pas administrateur et ne pouvez pas changer ce qui est installé sur ce système.

Bien entendu, vous pouvez télécharger le code source de bash et construire votre propre bash à partir de zéro, en le plaçant dans le répertoire ~/bin par exemple. Et vous pouvez également modifier votre $PATH dans votre .bash_profile pour inclure ~/bin comme première entrée ( PATH=$HOME/bin:$PATH como ~ ne se développera pas en $PATH ). Si vous appelez maintenant bash le shell le cherchera d'abord dans $PATH dans l'ordre, donc il commence par ~/bin où il trouvera votre bash . La même chose se produit si les scripts recherchent bash en utilisant #!/usr/bin/env bash Ainsi, ces scripts fonctionneraient maintenant sur votre système en utilisant votre code d'accès personnalisé. bash construire.

Un inconvénient est que cela peut conduire à un comportement inattendu, par exemple, le même script sur la même machine peut s'exécuter avec différents interprètes pour différents environnements ou utilisateurs avec différents chemins de recherche, causant toutes sortes de maux de tête.

Le plus gros inconvénient avec env est que certains systèmes ne permettent qu'un seul argument, donc vous ne pouvez pas faire ceci #!/usr/bin/env <interpreter> <arg> comme les systèmes le verront <interpreter> <arg> comme un seul argument (ils le traiteront comme si l'expression était citée) et donc env cherchera un interprète nommé <interpreter> <arg> . Notez que ce n'est pas un problème de la env elle-même, qui permet toujours le passage de plusieurs paramètres, mais avec l'analyseur Shebang du système qui analyse cette ligne avant même d'appeler la commande 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 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 un environnement propre ou $PATH a été exclu du nettoyage. Permettez-moi d'en faire la démonstration :

Habituellement /bin est un endroit bien protégé, seulement root est capable d'y changer quoi que ce soit. Votre répertoire personnel ne l'est pas, cependant, tout 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 Ainsi, tous les scripts utilisant #!/usr/bin/env bash finira par fonctionner avec ce faux bash . Si sudo garde $PATH vous avez de gros problèmes.

Par exemple, considérons 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 simple script. 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 shells classiques doivent tous être situés dans le répertoire /bin et si vous ne voulez pas les placer là pour une raison quelconque, il n'y a pas de problème à placer un lien symbolique dans le répertoire /bin qui pointe vers leurs emplacements réels (ou peut-être /bin est lui-même un lien symbolique), j'utiliserais donc toujours #!/bin/sh y #!/bin/bash . Il y a trop de choses qui se briseraient si ces positions ne fonctionnaient plus. Ce n'est pas que POSIX exige ces positions (POSIX ne standardise pas les noms de chemin et donc ne standardise pas du tout la fonctionnalité shebang) mais elles sont si communes, que même si un système n'offrait pas une fonction /bin/sh il comprendrait probablement encore #!/bin/sh et savoir quoi en faire et que ce ne soit que pour la compatibilité avec le code existant.

Mais pour les interpréteurs plus modernes, non standard et optionnels comme Perl, PHP, Python ou Ruby, il n'est pas vraiment spécifié où ils doivent être situés. Ils peuvent être dans /usr/bin mais ils peuvent aussi bien être dans /usr/local/bin ou dans une branche hiérarchique complètement différente ( /opt/... , /Applications/... etc.). C'est pourquoi ces derniers utilisent souvent le #!/usr/bin/env xxx syntaxe shebang.

13voto

Scott Fay Points 5

Au lieu de définir explicitement le chemin vers l'interpréteur comme dans /usr/bin/bash/ En utilisant la commande env, l'interpréteur est recherché et lancé à partir de l'endroit où il est trouvé pour la première fois. Ceci a à la fois avantages et inconvénients

0 votes

Sur la plupart des systèmes, ils seront fonctionnellement les mêmes, mais cela dépend de l'emplacement de vos exécutables bash et env. Je ne sais pas comment cela affectera les variables d'environnement, cependant.

2 votes

"Il est possible de spécifier l'interpréteur sans utiliser env, en donnant le chemin complet vers l'interpréteur. Un problème est que sur différents systèmes informatiques, le chemin exact peut être différent. En utilisant plutôt env, l'interpréteur est recherché et localisé au moment où le script est exécuté. Cela rend le script plus portable, mais augmente également le risque que le mauvais interprète soit sélectionné car il recherche une correspondance dans chaque répertoire du chemin de recherche de l'exécutable. Il souffre également du même problème dans la mesure où le chemin vers le binaire env peut également être différent sur une base par machine."-Wikipedia

4voto

sudo97 Points 298

Je trouve ça utile, parce que quand je ne connaissais pas env, avant de commencer à écrire script je faisais ça :

type nodejs > scriptname.js #or any other environment

et ensuite je modifiais cette ligne dans le fichier en shebang.
Je faisais cela, 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 a-t-il des détails à ce sujet, mais voici ma raison

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X