Pourquoi #! / Usr / bin / env bash est-il supérieur à #! / Bin / bash?

212

J'ai vu dans un certain nombre d'endroits, y compris des recommandations sur ce site ( quel est le shebang Bash préféré? ), À utiliser #!/usr/bin/env bashde préférence #!/bin/bash. J'ai même vu une personne entreprenante suggérer que l'utilisation #!/bin/bashétait incorrecte et la fonctionnalité bash serait perdue en faisant cela.

Cela dit, j'utilise bash dans un environnement de test étroitement contrôlé où chaque disque en circulation est essentiellement un clone d'un seul disque maître. Je comprends l'argument de la portabilité, bien qu'il ne soit pas nécessairement applicable dans mon cas. Y a-t-il une autre raison de préférer #!/usr/bin/env bashles alternatives et, en supposant que la portabilité était un problème, y a-t-il une raison de l'utiliser pourrait casser la fonctionnalité?

spugm1r3
la source
7
Ce n'est pas forcément mieux. Voir cette question et ma réponse sur unix.stackexchange.com. (Je voterais pour fermer ceci en double, mais je ne pense pas que vous puissiez le faire sur tous les sites.)
Keith Thompson
2
En plus de la réponse de @ zigg, envpeut ne pas se trouver à /usr/bin. Les commentaires de Shebang sont tout à fait une mauvaise idée à mon humble avis. Si votre interpréteur de script par défaut ne gère pas les commentaires shebang, il s'agit simplement d'un commentaire. Cependant, si vous savez que l'interpréteur de script peut gérer les commentaires shebang et que vous connaissez le chemin d'accès à bash, il n'y a aucune raison de ne pas l'invoquer en utilisant son chemin absolu, sauf si le chemin est trop long (peu probable), ou vous pouvez éventuellement porter le script à un système qui n'a pas bash situé dans / bin. Là encore, les mises en garde que j'ai mentionnées précédemment s'appliquent dans ce cas car cela implique la portabilité.
1
@KeithThompson, merci pour le lien. Peut-être que ma recherche d'une réponse avant de poster la question était un peu étroite. Ce que je retiens de tout cela: (1) linux / unix / posix / etc ... est gris, et (2) toute personne prétendant avoir absolument la bonne réponse a absolument la bonne réponse pour son scénario particulier.
spugm1r3
3
Le comportement de beaucoup de choses dans POSIX / Unix est bien défini. Les emplacements ne sont pas toujours aussi clairs. Quelque chose doit exister comme /etcou /bin/sh. bashest un module complémentaire pour la plupart des systèmes de type Unix. Ce n'est que Linux où il bashest garanti d'être intégré /binet très probablement également lié en tant que /bin/sh. Depuis que Linux est devenu l'Unix de facto moderne pour beaucoup de gens, le fait qu'il puisse exister des systèmes autres que Linux a été oublié. Dans ma propre réponse ci-dessous, j'ai supposé Linux parce que vous avez dit bash. Beaucoup de boîtiers BSD avec lesquels j'ai travaillé ne l'ont même pas installé.
Sean Perry
2
@Keith - Dans le cas de Bash (par opposition à Python dans les autres questions) ... OpenBSD n'en a pas /bin/bash. Bash n'est pas installé par défaut. Si vous le voulez, vous devez pkg install bash. Une fois installé, il se trouve à /usr/local/bin/bash. Il n'y a rien installé /bin/bashsur OpenBSD. Un coup de #!/bin/bashvolonté d'erreur, et #!/usr/bin/env bashréussira.
JWW

Réponses:

229

#!/usr/bin/envrecherches PATHpour bash, et bashne sont pas toujours /bin, en particulier sur les systèmes non-Linux. Par exemple, sur mon système OpenBSD, c'est dans/usr/local/bin , car il a été installé en tant que package facultatif.

Si vous êtes absolument sûr que bashc'est /binet que vous le serez toujours, il n'y a aucun mal à le mettre directement dans votre shebang - mais je le déconseille car les scripts et les programmes ont tous une vie au-delà de ce que nous pensons initialement.

zigg
la source
29
qu'en est-il de l' envemplacement? POSIX ne le force pas.
Julio Guerra
1
@JulioGuerra Tout comme avoir un binaire /usr/lib/sendmail(ou, plus récemment, /usr/sbin/sendmail) disponible pour traiter le courrier, il est dans l'intérêt d'un système de type Unix, /usr/bin/envcar le shebang env est une telle pratique courante. Il s'agit d'une interface standard de facto.
zigg
8
@zigg C'est tellement UN * X ... <-: Je veux dire, il est dans leur intérêt d'avoir un emplacement standard pour env, mais en quelque sorte de ne pas avoir un emplacement standard (qui peut simplement être un lien logiciel) pour bash. Sans parler de la raison pour laquelle hashbang n'accepte pas simplement #!bash, et utilise le PATH, au lieu de faire exactement la même chose avec env. Pas assez confus pour les débutants, je suppose.
ddekany
1
@JulioGuerra Selon wikipedia, vous êtes vrai: ce n'est pas "garanti". Au contraire, cela semble "plus probable". Wikipedia dit This mostly works because the path /usr/bin/env is commonly used for the env utilityici en.wikipedia.org/wiki/Shebang_(Unix) - Nous devons nous confier d' envêtre là "probablement" dans tous les systèmes.
Xavi Montero
2
@XaviMontero: J'ai utilisé un système où se envtrouvait /bin, pas dans /usr/bin( je ne sais pas lequel, probablement SunOS 4). Ces jours-ci, il /usr/bin/envsera très probablement disponible, simplement en raison de la popularité du #!/usr/bin/envhack.
Keith Thompson
39

L'emplacement standard de bash est /bin , et je pense que c'est vrai sur tous les systèmes. Cependant, que faire si vous n'aimez pas cette version de bash? Par exemple, je veux utiliser bash 4.2, mais le bash sur mon Mac est à 3.2.5.

Je pourrais essayer de réinstaller bash dans /binmais cela peut être une mauvaise idée. Si je mets à jour mon système d'exploitation, il sera remplacé.

Cependant, je pouvais installer bash dans /usr/local/bin/bashet configurer mon PATH pour:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

Maintenant, si je précise bash, je ne reçois pas l'ancien cruddy /bin/bash, mais le plus récent et plus brillant à/usr/local/bin . Agréable!

Sauf que mes scripts shell ont ça !# /bin/bash shebang. Ainsi, lorsque j'exécute mes scripts shell, j'obtiens cette ancienne et moche version de bash qui n'a même pas de tableaux associatifs.

Utiliser /usr/bin/env bashutilisera la version de bash trouvée dans mon PATH. Si je configure mon CHEMIN, pour que/usr/local/bin/bash soit exécuté, c'est le bash que mes scripts utiliseront.

Il est rare de voir cela avec bash, mais c'est beaucoup plus courant avec Perl et Python:

  • Certaines versions Unix / Linux qui se concentrent sur la stabilité sont parfois loin derrière avec la sortie de ces deux langages de script. Il n'y a pas longtemps, RHEL Perl était à 5.8.8 - une version de huit ans de Perl! Si quelqu'un voulait utiliser des fonctionnalités plus modernes, vous deviez installer votre propre version.
  • Des programmes comme Perlbrew et Pythonbrew vous permettent d'installer plusieurs versions de ces langues. Ils dépendent de scripts qui manipulent votre PATH pour obtenir la version souhaitée. Le codage dur du chemin signifie que je ne peux pas exécuter mon script sous brew .
  • Il n'y a pas si longtemps (d'accord, c'était il y a longtemps) que Perl et Python n'étaient pas des packages standard inclus dans la plupart des systèmes Unix. Cela signifiait que vous ne saviez pas où ces deux programmes étaient installés. C'était sous /bin? /usr/bin? /opt/bin? Qui sait? L'utilisation #! /usr/bin/env perlsignifiait que je n'avais pas besoin de savoir.

Et maintenant pourquoi vous ne devriez pas utiliser #! /usr/bin/env bash

Lorsque le chemin est codé en dur dans le shebang, je dois courir avec cet interprète. Ainsi, #! /bin/bashm'oblige à utiliser la version installée par défaut de bash. Étant donné que les fonctionnalités bash sont très stables (essayez d'exécuter une version 2.x d'un script Python sous Python 3.x), il est très peu probable que mon script BASH particulier ne fonctionne pas, et puisque mon script bash est probablement utilisé par ce système et d'autres systèmes , l'utilisation d'une version non standard de bash peut avoir des effets indésirables. Il est très probable que je veux m'assurer que la version standard stable de bash est utilisée avec mon script shell. Ainsi, je veux probablement coder en dur le chemin dans mon shebang.

David W.
la source
5
Ce n'est pas vrai: «L'emplacement standard de bash est / bin» (à moins que vous ne puissiez citer un document standard), il est peut-être plus précis que c'est l'emplacement «habituel» sur la plupart des distributions Linux et macos, mais ce n'est pas un standard sur les systèmes Unix en général (et n'est notamment pas l'emplacement sur la plupart des * bsds).
tesch1
13

Pour l'invoquer, bashc'est un peu exagéré. Sauf si vous avez plusieurs bashbinaires comme le vôtre dans ~ / bin, mais cela signifie également que votre code dépend de $ PATH contenant les bonnes choses.

C'est pratique pour des choses comme ça python. Il existe des scripts et des environnements wrapper qui mènent à une alternativepython binaires .

Mais rien n'est perdu en utilisant le chemin exact vers le binaire tant que vous êtes sûr que c'est le binaire que vous voulez vraiment.

Sean Perry
la source
Repérez pour python. J'ai perdu le compte du nombre de places pythonpeut être trouvé 😀
zigg
2
D'accord, c'est /usr/bin/envplus utile pour Python, surtout si vous utilisez virtualenv.
Dennis
10

Il y a beaucoup de systèmes qui n'ont pas Bash /bin, FreeBSD et OpenBSD pour n'en nommer que quelques-uns. Si votre script est censé être portable à beaucoup de différents systèmes Unix, vous pouvez utiliser au #!/usr/bin/env bashlieu de#!/bin/bash .

Notez que cela n'est pas vrai pour sh; pour les scripts compatibles Bourne J'utilise exclusivement #!/bin/sh, puisque je pense à peu près tous les Unix existants a shen /bin.

James Ko
la source
Dans Ubuntu 18.04 /bindir, je vois sh -> dash. Lien symbolique avec dash, révélant la nature Debian d'Ubuntu. Exécutez ces trois chaînes de commande pour vous rendre compte que tout se résume aux préférences individuelles: which bashpuis which shalors which dash.
noobninja
0

Je préfère envelopper le programme principal dans un script comme ci-dessous pour vérifier tous les bashdisponibles sur le système. Mieux vaut avoir plus de contrôle sur la version qu'il utilise.

#! /usr/bin/env bash

# This script just chooses the appropriate bash
# installed in system and executes testcode.main

readonly DESIRED_VERSION="5"

declare all_bash_installed_on_this_system
declare bash

if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
    found=0

    all_bash_installed_on_this_system="$(\
        awk -F'/' '$NF == "bash"{print}' "/etc/shells"\
        )"

    for bash in $all_bash_installed_on_this_system
    do
        versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
        [ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
    done
    if [ "${found}" -ne 1 ]
    then
        echo "${DESIRED_VERSION} not available"
        exit 1
    fi
fi

$bash main_program "$@"
Mihir
la source
0
 #!/usr/bin/env bash

est certainement meilleur car il trouve le chemin exécutable bash à partir de votre variable d'environnement système.

Accédez à votre shell Linux et tapez

env

Il imprimera toutes vos variables d'environnement.

Accédez à votre script shell et tapez

echo $BASH

Il imprimera votre chemin bash (selon la liste des variables d'environnement) que vous devez utiliser pour créer votre chemin de shebang correct dans votre script.

kta
la source