Réponse 1:

Programmation d'apprentissage: Quelle est la différence entre «#! / usr / bin / env bash "et" #! / usr / bin / bash "?

L'action décrite par Ondřej Vagner dans sa réponse (que son système d'exploitation peut trouver le programme bash sans savoir où il est stocké) se produit en raison d'un effet secondaire de la façon dont l'appel système exec (2) et le programme env (1) sont mis en œuvre, mais sa réponse n'est en fait pas tout à fait correcte en décrivant la différence.

Trop de son crédit de ce côté est un malentendu commun de nombreuses personnes, qui découvrent un cas d'action / d'utilisation et ne comprennent pas entièrement ce qui est arrivé en premier (ou comme nous aimons à dire dans «le biz» ce qu'est le «bit d'ordre élevé», en plus d'être honnête à ce sujet en termes Unix, quelqu'un utilisant uniquement Unix depuis 2011 est un peu un nouveau venu dans le monde Unix et peut ne pas encore comprendre pleinement la vraie raison de certaines de ces conventions et opérations).

Sérieusement, ne pas reprendre la réponse d'Ondřej puisque vous apprenez à programmer ce serait une bonne idée de comprendre quelle est réellement la différence et quel est l'effet secondaire qui provoque le comportement qu'il observe; afin que vous puissiez utiliser la fonction pour ce à quoi elle est destinée, y compris l'effet secondaire; ne pensez pas seulement à l'effet secondaire.

Avant de continuer, je dois vous rappeler que depuis le début de son développement à la fin des années 60 / début des années 70, la famille de systèmes d'exploitation Unix contient un outil de documentation extrêmement utile appelé les pages de manuel; qui est accessible avec la commande man (1). [1] Je recommande fortement de vous familiariser avec ce merveilleux outil, mais les règles du vieil adage "Google est votre ami" s'appliquent toujours.

Tout d'abord, vous devez comprendre comment les programmes sont démarrés (ie exec (2) 'ed) et ensuite nous discuterons du #! concept (souvent, comme je le dirai plus tard, appelé «shebang») idée; et enfin ce que fait le programme env (1).

Donc… lorsque le système d'exploitation doit charger un programme en mémoire, il doit trouver le fichier binaire associé à ce programme et en copier «suffisamment en mémoire pour démarrer le programme», puis forcer le processeur à passer au programme « de départ ». L'appel système Unix exec (2) (qui a un certain nombre de saveurs dont je ne parlerai pas ici), est la fonction de base utilisée pour faire de même. L'un des paramètres de l'appel de fonction est le nom du fichier programme que l'OS doit charger.

Étant donné que les systèmes d'exploitation modernes comme UNIX ont un système de fichiers hiérarchique [2], nous pouvons décrire le «chemin» nécessaire que le système doit suivre à travers le système de fichiers pour trouver le fichier programme. Si nous lui donnons le chemin précis (et qu'un fichier programme correct existe à cet emplacement), le fichier est chargé en mémoire et démarré. Sinon, une erreur est renvoyée par l'OS à l'appelant de exec (2). Mais nous ne savons souvent pas ce que le fichier de type est stocké là (c'est un vrai programme `` exécutable '' ou non) et plus probablement nous ne savons pas où le fichier est réellement stocké, nous voulons donc que le système d'exploitation le trouve pour moi et vérifiez-le.

Réfléchissez maintenant un instant au fonctionnement des humains par opposition au fonctionnement des ordinateurs. Pensez à acheter une miche de pain au supermarché. Vous écrivez sur votre liste de courses «pain» et non «Dave's Market, Bakery, Allée 2, première étagère, à 3 pieds de la fin, du pain. «Imaginez un acheteur robotique, s'il voit du« pain », il faut lui dire toutes les autres informations pour trouver l'article« pain ».

Il en va de même pour le fichier programme. Si nous tapons simplement 'commande' (dans votre cas, 'bash' mais cela peut être 'python' ou 'awk' ou 'perl' ou l'un des nombreux programmes de commande fournis par Unix.

Se pose donc la question: où se trouve ce fichier dans la hiérarchie du système de fichiers? Donc, l'idée de la variable PATH a été créée, qui indique à la fonction système exec (2) de regarder le premier caractère de la commande donnée, si ce n'est pas une `` barre oblique '' (/), puis prenez une chaîne du PATH, ajoutez à la commande, en le séparant par une barre oblique et essayez comme nouveau nom de fichier pour voir si le système d'exploitation trouve un «fichier exécutable» à l'emplacement défini par ce nouveau nom de fichier. Notez également que l'environnement PATH contient un certain nombre de chemins différents (séparés par deux points) pour essayer de se préparer avant de trouver un «fichier exécutable» avant que le système d'exploitation n'abandonne et renvoie une erreur.

Maintenant, ce schéma d'emplacement de fichier exécutable a quelques problèmes, le plus important est s'il y a plus d'un nom de fichier «bash» dans votre cas, qui peut être trouvé dans les différents chemins d'accès qui sont spécifiés, quelle version du fichier devrait le système d'exploitation utilisation? La réponse Unix traditionnelle est appelée «premier ajustement», c'est-à-dire le premier nom de chemin correspondant au système d'exploitation et est utilisé comme fichier exécutable. Ce schéma signifie que les chemins d'accès spécifiés dans la variable PATH sont ordonnés.

Le deuxième problème est que ce qui se passe si le fichier que nous trouvons décrit par ce nouveau nom de fichier n'est pas un programme exécutable «binaire» (c'est-à-dire un fichier binaire d'instructions machine pour ce CPU), mais plutôt autre chose, comme par exemple un texte ASCII fichier. C'est là que les idées d'un «nombre magique» Unix et de la convention «shebang» entrent en jeu.

Avec Unix, les premiers octets stockés à l'intérieur du fichier identifient le contenu du fichier. Ceci est différent des autres OS du jour, qui suivaient une convention selon laquelle le contenu du fichier était déterminé par le nom du fichier. Par exemple, les anciens systèmes d'exploitation DEC utilisaient la convention ultérieure (c'est-à-dire VMS, TOP et surtout RT11, que Microsoft continuerait à émuler à partir de leur héritage RT11 → CP / M → DOS).

Étant donné qu'Unix a codé le «type de fichier» dans le contenu du fichier [3], cela signifiait que la mise en œuvre de l'appel système exec (2) à l'intérieur du système d'exploitation pouvait atteindre un pic dans les premiers octets du fichier pour déterminer quoi faire. . À l'origine, le système d'exploitation Unix cherchait à voir s'il s'agissait d'un «fichier a.out» qui était le premier format de fichier UNIX pour les fichiers exécutables binaires PDP-11. Mais à partir de la fin des années 1970, l'équipe de UC Berkeley a implémenté / ajouté une fonction / convention à l'appel exec. Si les deux premiers octets étaient les caractères, # !, les octets suivants jusqu'à la première nouvelle ligne seraient interprétés par le SE UNIX comme un nouveau chemin d'accès du programme à exec (2) à la place [4] et le fichier actuel qui venait d'être ouvert par UNIX se verrait remettre ce deuxième programme comme entrée standard [pour plus d'informations sur les E / S, voir aussi: entrée / sortie du fichier C - Wikipedia].

Cette nouvelle extension de l'appel système UNIX exec (2) a permis de créer des scripts shell exécutables. Il a également été exploité lorsque des shells alternatifs sont apparus tels que l'UCB csh (1) et plus tard le bash de Gnu (1) et le shebang a finalement été utilisé pour prendre en charge de `` petits langages '' tels que awk (1), perl (1) et python (1 ) quand ils sont apparus.

Ok, alors qu'est-ce que cela a à voir avec env (1) et un effet secondaire? Comme je l'ai décrit précédemment, exec (2) prend en charge une chaîne appelée PATH qui est utilisée pour décider des noms de chemin à ajouter à la commande pour rechercher un fichier exécutable. À l'origine, la définition de cette chaîne n'était pas facile à faire, mais beaucoup de gens voulaient une certaine flexibilité pour le faire. De même, le système d'exploitation voulait un moyen de transmettre des informations dans une programmation en cours qui pouvait être réglée par l'utilisateur, mais n'avait pas besoin d'être retapée à chaque démarrage d'un programme. Ainsi, à partir de la septième édition d'Unix, l'idée du programme «environnement» a été créée et un schéma a été créé pour passer des chaînes ASCII dans le programme appelées variables d'environnement qui étaient réglables par l'utilisateur; avec le CHEMIN est l'un des plus importants car il a permis de passer des chaînes des chemins de noms de fichiers pour essayer avec l'appel exec (2) (avant cela, le CHEMIN était défini par l'OS et était plus difficile à changer).

La nouvelle fonctionnalité `` environnement '' était alors très populaire, mais presque immédiatement, les gens voulaient un moyen de modifier l'une des deux variables d'environnement juste pour exécuter une commande spécifique, mais ne pas être mémorisé / réutilisé sur commande après celle-là. Ainsi, la commande env (1) a été créée avec la syntaxe:

env EVAR1 = evar1_value .. EVARN = evarn_value programme p1 ... pn

Lorsque le programme env s'exécute, il prend l'environnement hérité du système d'exploitation, puis ajoute ou modifie les variables répertoriées dans la commande, puis il appelle le programme exec (2) avec ses paramètres en utilisant l'environnement nouvellement modifié.

Réfléchissez donc à ce qui se passe; lorsque la commande devient:

programme env 

ou dans votre cas: env bash

La commande env ne change pas l'environnement, mais oblige simplement le programme à être appelé via exec (2) en utilisant ses règles d'expansion de nom de chemin; ce qui, comme Ondřej l'a observé, signifie que si vous avez un chemin riche avec beaucoup de chemins facultatifs à essayer, vous pouvez placer bash ailleurs que / usr / bin [qui est l'endroit où des coquilles telles que bash vivaient à l'origine quand il a été créé par le projet Gnu] et le binaire exécutable bash sera trouvé par le système d'exploitation pour le programme appelant.

De plus, c'est généralement moins un problème, car d'autres formes de shebang fonctionneront, mais l'astuce env dans le fichier script lui-même comme décrit ci-dessus est apparue lorsque Python est arrivé sur la scène UNIX parce que Python était souvent installé en privé et donc pas dans le PATH de chaque utilisateur, ainsi que les répertoires de fichiers réels sur lesquels Python était souvent installé variaient souvent. C'est moins un problème aujourd'hui car les interprètes utilisés pour les fichiers texte exécutables sont plus souvent dans des emplacements standard pour les répertoires de programmes exécutables.

[1] L'une des meilleures descriptions de qui cela fonctionne et de comment l'utiliser vient de l'Indiana University: Utilisation de la commande man Unix pour lire les pages de manuel. FWIW: Linux est une implémentation moderne d'Unix comme je l'ai décrit ailleurs, mais cette fonctionnalité est même disponible lorsque l'attribut facultatif du sous-système Windows pour Linux de Windows 10 est activé en tapant PowerShell en tant qu'administrateur: Enable-WindowsOptionalFeature -Online -FeatureName Microsoft -Windows-Subsystem-Linux

[2] Certains d'entre nous se souviennent et ont utilisé des systèmes qui n'avaient pas de système de fichiers hiérarchique. Même les petits systèmes d'exploitation construits dans les années 1970, comme le DOS de Microsoft, sont venus à l'idée tardivement et c'est également l'une des raisons pour lesquelles Microsoft a utilisé la barre oblique inverse comme séparateur de fichiers, pas le caractère barre oblique qui était déjà standard sur les grands systèmes à l'époque.

[3] Pour plus de détails sur des choses comme l'idée derrière les types de fichiers et la magie, consultez les commandes: "man 5 magic" et "man 1 file".

[4] Ces caractères ont été choisis parce que dans le shell Unix si le premier caractère est un "signe numérique" (# - ou "marque de hachage" ou "signe dièse"), alors tous les caractères jusqu'à la première nouvelle ligne sont ignorés, considérés un «commentaire», sans quoi le shell n'a pas réagi.

Modifié le 5/2/2019 pour corriger quelques fautes de frappe et clarifier une langue. Dyslexie-R-Me


Réponse 2:

La différence est petite, mais significative.

#! / usr / bin / bash

Cela indique au système: «Exécutez l'exécutable bash qui se trouve dans / usr / bin».

#! / usr / bin / env bash

Cela indique au système: «Découvrez à partir de votre environnement où votre exécutable bash est installé et exécutez-le à partir de là.»

Sur mon PC, bash est installé dans / bin / bash. Il est considéré par les mainteneurs de distribution comme étant suffisamment crucial pour que le système s'y trouve. Si j'essayais d'exécuter un script avec le shebang / usr / bin / bash, cela échouerait. D'un autre côté, / usr / bin / env exécute simplement le bash que mon système connaît.