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

372

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 consulté la env page de manuel , j'obtiens cette définition:

 env - run a program in a modified environment

Qu'est-ce que ça veut dire?

tarrsalah
la source
7
Voir cette question et ma réponse .
Keith Thompson
6
Qui peut me dire pourquoi cette question est fermée, "liée à la programmation ou au développement de logiciels" ne l'est pas?
tarrsalah
1
Je suis d'accord que ce n'est pas hors sujet, mais c'est probablement un double de plusieurs autres questions comme celle-ci .
Keith Thompson
16
Cette question n'aurait pas dû être marquée comme hors sujet. Il a juste besoin de 5 personnes avec un score supérieur à 3000 pour le marquer comme "sur le sujet" et il peut être rouvert. C'est une question - spécifiquement sur la programmation.
Danijel-James W
3
Je suis choqué. Choqué de constater que la documentation Linux regorge de tautologies. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Réponses:

323

L'exécution d'une commande /usr/bin/enva l'avantage de rechercher quelle que soit la version par défaut du programme dans votre environnement actuel .

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 emplacements 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 -Ssur certaines versions de envpour contourner ce problème, mais le script deviendra alors encore moins portable et se brisera sur des systèmes assez récents (par exemple, même Ubuntu 16.04 sinon plus tard).

Un autre inconvénient est que, comme 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 a réussi à faire appeler son exécutable bashsur 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 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, cette dernière serait préférable, car elle limite les possibilités d'injection de code.

Alec Bennett
la source
22
Un autre inconvénient est que vous ne pouvez pas passer d'argument supplémentaire à l'interpréteur.
Keith Thompson
1
@KeithThompson: Information incorrecte. Vous pouvez passer des options à l'interpréteur sous-jacent en utilisant / usr / bin / env!
Gaurav Agarwal
4
@GauravAgarwal: Pas sur mon système. Un script contenant juste cette seule ligne: se #!/usr/bin/env echo Helloplaint: /usr/bin/env: echo Hello: No such file or directory. Apparemment, il traite echo Hellocomme un seul argument /usr/bin/env.
Keith Thompson
1
@ AndréLaszlo: La envcommande permet certainement de passer des arguments à la commande. Le problème est la sémantique de la #!ligne, et cela dépend du noyau. Les noyaux Linux récents permettent des choses comme #!/usr/bin/env command args, mais les noyaux Linux plus anciens et d'autres systèmes ne le permettent pas.
Keith Thompson
1
Pourquoi y a-t-il des retours en arrière dans le bloc de code? Ne devraient-ils pas être supprimés?
Benjamin W.
64

L'utilisation #!/usr/bin/env NAMEpermet 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.

Dolphiniac
la source
9
Au moins, vous devez savoir où se trouve env :).
ᐅ devrimbaris
1
Excellente réponse. Explique succinctement ce que fait le shebang env, plutôt que de dire "choisit le programme en fonction de la configuration de votre système"
De Novo
13

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é d'où qu'il se trouve pour la première fois. Cela a des avantages et des inconvénients

safay
la source
Sur la plupart des systèmes, ils seront fonctionnellement identiques, mais cela dépend de l'emplacement de vos exécutables bash et env. Je ne sais pas cependant comment cela affectera les variables d'environnement.
safay
2
"Il est possible de spécifier l'interpréteur sans utiliser env, en donnant le chemin complet à 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ète est recherché et localisé à l'heure à laquelle 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 exécutable. Il souffre également du même problème en ce que le le chemin vers le binaire env peut également être différent par machine. "- Wikipedia
Mike Clark
10

Si les scripts shell commencent par #!/bin/bash, ils seront toujours exécutés avec bashfrom /bin. S'ils commencent cependant par #!/usr/bin/env bash, ils chercheront bashdans $PATHpuis commenceront par le premier qu'ils peuvent trouver.

Pourquoi cela serait-il utile? Supposons que vous vouliez exécuter des bashscripts, qui nécessitent bash 4.x ou plus récent, mais votre système n'a que bash3.x installé et actuellement votre distribution n'offre pas une version plus récente ou vous n'êtes pas administrateur et ne pouvez pas changer ce qui est installé sur ce système .

Bien sûr, vous pouvez télécharger le code source bash et créer votre propre bash à partir de zéro, en le plaçant ~/binpar exemple. Et vous pouvez également modifier votre $PATHvariable dans votre .bash_profilefichier pour l'inclure ~/bincomme première entrée ( PATH=$HOME/bin:$PATHcar ~elle ne sera pas développée dans $PATH). Si vous appelez maintenant bash, le shell va d'abord le chercher $PATHdans l'ordre, il commence donc par ~/binoù il trouvera votre fichier bash. La même chose se produit si les scripts recherchent l' bashutilisation #!/usr/bin/env bash, de sorte que ces scripts fonctionneraient désormais sur votre système à l'aide de votre bashgénération personnalisée .

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 des chemins de recherche différents, provoquant toutes sortes de maux de tête.

Le plus gros inconvénient envest que certains systèmes n'autorisent qu'un seul argument, vous ne pouvez donc pas le faire #!/usr/bin/env <interpreter> <arg>, car les systèmes le verront <interpreter> <arg>comme un seul argument (ils le traiteront comme si l'expression était citée) et envrechercheront donc un interprète nommé <interpreter> <arg>. Notez que ce n'est pas un problème de la envcommande elle-même, qui a toujours permis de passer plusieurs paramètres mais avec l'analyseur shebang du système qui analyse cette ligne avant même d'appeler env. Pendant ce temps, cela a été corrigé sur la plupart des systèmes, mais si votre script veut être ultra portable, vous ne pouvez pas compter que cela a été corrigé sur le système que vous exécuterez.

Il peut même avoir des implications sur la sécurité, par exemple s'il sudon'a pas été configuré pour nettoyer l'environnement ou a $PATHété exclu du nettoyage. Permettez-moi de le démontrer:

/binEst généralement un endroit bien protégé, seul rootpeut y changer quoi que ce soit. Cependant, votre répertoire personnel n’est pas un programme que vous exécutez peut y apporter des modifications. Cela signifie qu'un code malveillant pourrait placer un faux bashdans un répertoire caché, modifiez votre .bash_profilepour inclure ce répertoire dans votre $PATH, afin que tous les scripts utilisant #!/usr/bin/env bashfinissent par fonctionner avec ce faux bash. Si ça sudocontinue $PATH, vous avez de gros ennuis.

Par exemple, considérez qu'un outil crée un fichier ~/.evil/bashavec 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ù se sudoconserve $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 /binet 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 /binqui pointe vers leur emplacement réel (ou peut /bin- être lui - même est un lien symbolique), donc J'irais toujours avec #!/bin/shet #!/bin/bash. Il y a trop de choses qui se briseraient si elles ne fonctionnaient plus. Ce n'est pas que POSIX aurait besoin de ces positions (POSIX ne standardise pas les noms de chemin et donc il ne standardise même pas du tout la fonctionnalité shebang) mais ils sont si courants que même si un système ne proposait pas de /bin/sh, il comprendrait probablement #!/bin/shet savoir quoi en faire et que ce ne soit que pour la compatibilité avec le code existant.

Mais pour les interprètes optionnels plus modernes et non standard comme Perl, PHP, Python ou Ruby, il n'est pas vraiment spécifié où ils devraient être situés. Ils peuvent être en , /usr/binmais ils peuvent aussi bien être /usr/local/binou dans une branche de la hiérarchie complètement différente ( /opt/..., /Applications/..., etc.). C'est pourquoi ceux-ci utilisent souvent la #!/usr/bin/env xxxsyntaxe shebang.

Mecki
la source
4

Je le trouve utile, car quand je ne connaissais pas env, avant de commencer à écrire un script, je faisais ceci:

type nodejs > scriptname.js #or any other environment

et puis je modifiais cette ligne dans le fichier en shebang.
Je faisais cela, parce que je ne me souvenais pas toujours où se trouve nodejs sur mon ordinateur - / usr / bin / ou / bin /, donc pour moi, envc'est très utile. Peut-être qu'il y a des détails avec ça, mais c'est ma raison

sudo97
la source