Y a-t-il une différence entre "." et "source" dans bash, après tout?

30

Je cherchais la différence entre le "." et les commandes intégrées "source" et quelques sources (par exemple, dans cette discussion , et la page de manuel bash ) suggère que ce sont les mêmes.

Cependant, suite à un problème avec les variables d'environnement, j'ai effectué un test. J'ai créé un fichier testenv.sh qui contient:

#!/bin/bash
echo $MY_VAR

À l'invite de commande, j'ai effectué les opérations suivantes:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345

[notez que le 1er formulaire a renvoyé une chaîne vide]

Donc, cette petite expérience suggère qu’il ya une différence après tout, où pour la commande "source", l’environnement enfant hérite de toutes les variables du parent, où se trouve le "." ça ne marche pas.

Est-ce que quelque chose me manque ou est-ce une fonctionnalité non documentée / obsolète de bash ?

[GNU bash, version 4.1.5 (1) -release (x86_64-pc-linux-gnu)]

    
posée ysap 30.08.2012 - 03:07
la source

2 réponses

55

Réponse courte

Dans votre question, la deuxième commande n'utilise ni le composant intégré . du shell, ni la fonction intégrée source . Au lieu de cela, vous exécutez le script dans un shell séparé, en l'appelant par nom (comme tout autre fichier exécutable). Cela lui donne un ensemble distinct de variables d'environnement (bien que si vous exportez une variable d'environnement dans son shell parent, cela sera inclus). Si vous remplacez / par un espace, cela l'exécutera avec la fonction intégrée . , ce qui équivaut à source .

Explication étendue

Ceci est la syntaxe de la fonction intégrée source shell, qui exécute le contenu d'un script dans le shell actuel (et donc avec les variables d'environnement du shell en cours):

source testenv.sh

Ceci est la syntaxe de la fonction intégrée . , qui fait la même chose que source :

. testenv.sh

Cependant, cette syntaxe exécute le script en tant que fichier exécutable, en lançant un nouveau shell pour l’exécuter:

./testenv.sh

Cela n'utilise pas la fonction intégrée . . Au contraire, . fait partie du chemin d'accès au fichier que vous exécutez. En règle générale, vous pouvez exécuter n'importe quel fichier exécutable dans un shell en l'appelant avec un nom contenant au moins un caractère / . Pour exécuter un fichier dans le répertoire en cours, le précéder de ./ est donc le moyen le plus simple. À moins que le répertoire actuel ne soit dans votre PATH , vous ne pouvez pas exécuter le script avec la commande testenv.sh . Cela permet d'empêcher des personnes d'exécuter accidentellement des fichiers dans le répertoire en cours lorsqu'ils ont l'intention d'exécuter une commande système ou un autre fichier existant dans un répertoire répertorié dans la variable d'environnement PATH .

Comme l’exécution d’un fichier par nom (plutôt qu’avec source ou . ) l’exécute dans un nouveau shell, il aura son propre ensemble de variables d’environnement. Les variables d'environnement héritent des variables d'environnement du processus appelant (dans ce cas, votre shell interactif). Cependant, pour qu'une variable d'environnement soit transmise au nouveau shell, l'un des suivants doit être le cas:

  • La variable d'environnement a été exportée. Utilisez le shell export intégré pour cela. Dans votre exemple, vous pouvez utiliser export MY_VAR=12345 pour définir et exporter la variable en une seule étape ou, si elle est déjà définie, vous pouvez simplement utiliser export MY_VAR .

  • La variable d'environnement est explicitement définie et transmise pour la commande que vous exécutez. Ce généralement accomplit cela:

    MY_VAR=12345 ./testenv.sh
    

La syntaxe ./ pour les scripts nécessite une ligne Hashbang pour fonctionner (correctement)

Soit dit en passant, notez que, lorsque vous appelez un exécutable par son nom comme ci-dessus (et non avec les fonctions intégrées . ou source shell), quel programme shell est utilisé pour l'exécuter pas généralement déterminé par quel shell vous le lancez. Au lieu de cela:

  • Pour les fichiers binaires, le noyau peut être configuré pour exécuter des fichiers de ce type particulier. Il examine les deux premiers octets du fichier pour un "nombre magique" qui indique le type d’exécutable binaire. Voici comment les binaires exécutables peuvent être exécutés.

    Ceci est, bien sûr, extrêmement important, car un script ne peut pas s'exécuter sans un shell ou un autre interpréteur, qui est un binaire exécutable! De plus, de nombreuses commandes et applications sont des binaires compilés plutôt que des scripts.

    ( #! est la représentation textuelle du "nombre magique" indiquant un texte exécutable.)

  • Pour les fichiers censés s'exécuter dans un shell ou un autre langage interprété, la première ligne ressemble à:

    #!/bin/sh
    

    /bin/sh peut être remplacé par n'importe quel autre shell ou interpréteur destiné à exécuter le programme. Par exemple, un programme Python pourrait commencer par la ligne:

    #!/usr/bin/python
    

    Ces lignes sont appelées hashbang, shebang et plusieurs autres noms similaires. Voir cette entrée FOLDOC , cette Article de Wikipedia et Is #! / Bin / sh lu par l'interpréteur ? pour plus d'informations.

  • Si un fichier texte est marqué exécutable et que vous l'exécutez depuis votre shell (comme ./filename ) mais il ne commence pas par #! , le noyau ne parvient pas à l'exécuter. Cependant, voyant que cela est arrivé, votre shell va essayer de l'exécuter en passant son nom à certains shell. Il y a peu d'exigences sur le shell quel qui est ( "le shell exécutera une commande équivalente à un shell appelé ..." ). En pratique , certains shells, notamment bash * - exécute une autre instance d'elle-même, tandis que d'autres utilisent /bin/sh .Je vous recommande vivement d'éviter cela et d'utiliser plutôt une ligne de hachage (ou d'exécuter le script en le transmettant à l'interpréteur souhaité, par exemple, bash filename ).

    * Manuel GNU Bash , 3.7.2 Recherche et exécution de commandes : "Si cette exécution échoue car le fichier n'est pas au format exécutable et le fichier n'est pas un répertoire, il est supposé être un script shell et le shell l'exécute comme décrit dans Shells Scripts . "

réponse donnée Eliah Kagan 30.08.2012 - 03:52
la source
13

Oui, il vous manque quelque chose.

Je pense que vous confondez le '.' cela signifie répertoire courant, comme dans ./testenv.sh et le '.' cela signifie source (qui est une commande intégrée). Donc, dans le cas où "." signifie source ce serait . ./testenv.sh . Faites-vous un sens?

Essayez donc ceci:

MY_VAR=12345 
. ./testenv.sh
    
réponse donnée user1477 30.08.2012 - 03:43
la source

Lire d'autres questions sur les étiquettes