GNU/Linux >> Tutoriels Linux >  >> Linux

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

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.


Linux
  1. Comment Linux gère-t-il plusieurs séparateurs de chemins consécutifs (/home////nom d'utilisateur///fichier) ?

  2. L'espace est-il autorisé entre # ! Et /bin/bash à Shebang ?

  3. Quelle est la différence entre /sbin/nologin et /bin/false ?

  4. Aucune raison d'avoir un Shebang pointant vers /bin/sh plutôt que /bin/bash ?

  5. Linux – Fusionner /usr/bin et /usr/sbin dans /bin (gnu/linux) ?

Pourquoi /bin/sh pointe-t-il vers /bin/dash et non /bin/bash ? ?

cmake --version pointe vers /usr/bin/cmake tandis que cmake pointe vers /usr/local/bin

Quand dois-je utiliser #!/bin/bash et quand #!/bin/sh ?

Différence entre /bin et /usr/bin

Déplacement du contenu de /bin vers /usr/bin, possible d'annuler ?

Les sites Web doivent-ils vivre dans /var/ ou /usr/ selon l'utilisation recommandée ?