web-dev-qa-db-fra.com

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

Je cherchais la différence entre le "." et les commandes intégrées "source" et quelques sources (par exemple, dans la discussion this , et la page de manuel bash ) suggèrent qu'elles sont identiques.

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

Dans 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 la 1ère forme a retourné une chaîne vide]

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

Est-ce que je manque quelque chose ou est-ce une fonctionnalité non documentée/déconseillée de bash ?

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

36
ysap

Réponse courte

Dans votre question, la deuxième commande n'utilise ni le code . intégré ni le nom sourcename__. Au lieu de cela, vous êtes réellement exécutant le script dans un shell distinct, en l'invoquant par son nom, comme vous le feriez avec tout autre fichier exécutable. Cela lui donne un ensemble distinct de variables (bien que si vous exportez une variable dans son Shell parent, il sera une variable d'environnement pour tout processus enfant , et donc sera inclus dans les variables d'un shell enfant ). Si vous modifiez le / en un espace, cela s'exécutera avec le . intégré, ce qui équivaut à sourcename__.

Explication étendue

Voici la syntaxe du sourceShell intégré, qui exécute le contenu d'un script dans le Shell actuel (et donc avec les variables du Shell actuel):

source testenv.sh

Voici la syntaxe du . intégré, qui fait la même chose que sourcename__:

. 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 le . intégré. . fait plutôt partie du chemin du 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. Sauf si le répertoire en cours se trouve dans votre PATHname__, vous ne pouvez pas exécuter le script avec la commande testenv.sh. Cela empêche les personnes d'exécuter accidentellement des fichiers du répertoire en cours lorsqu'elles 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 PATHname__.

Étant donné que l'exécution d'un fichier par son nom (plutôt que avec sourceou .) l'exécute dans un nouveau shell, celui-ci aura son propre ensemble de variables. Le nouveau shell hérite des variables d'environnement du processus appelant (qui est dans ce cas votre shell interactif) et ces variables d'environnement deviennent des variables shell dans le nouveau shell. Cependant, pour qu'une variable Shell soit transmise au nouveau Shell, l'un des éléments suivants doit être le cas:

  1. La variable Shell a été exportée, ce qui en fait une variable d’environnement. Utilisez le shell intégré exportpour cela. Dans votre exemple, vous pouvez utiliser export MY_VAR=12345 pour définir et exporter la variable en une étape. Si elle est déjà définie, vous pouvez simplement utiliser export MY_VAR.

  2. La variable Shell est explicitement définie et transmise pour la commande que vous exécutez, ce qui en fait une variable d'environnement pour la durée de la commande en cours d'exécution. Ceci habituellement ​​accomplit ceci:

    MY_VAR=12345 ./testenv.sh
    

    Si MY_VAR est une variable du shell qui n'a pas été exportée, vous pouvez même exécuter testenv.sh avec MY_VAR passé en tant que variable d'environnement par le réglant sur lui-même:

    MY_VAR="$MY_VAR" ./testenv.sh
    

./ La syntaxe pour les scripts nécessite le fonctionnement d'une ligne de hachage (correctement)

Soit dit en passant, notez que, lorsque vous appelez un exécutable par son nom, comme indiqué ci-dessus (et non avec les commandes intégrées . ou sourceShell), le programme Shell utilisé pour l’exécuter est not ​​généralement déterminé par quel shell vous le lancez. Au lieu:

  • 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 de quel type d’exécutable binaire il s’agit. Voici comment les fichiers 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 interpréteur ou un interpréteur, qui est un binaire exécutable! De plus, de nombreuses commandes et applications sont des fichiers 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 dans un autre langage interprété, la première ligne est la suivante:

    #!/bin/sh
    

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

    #!/usr/bin/python
    

    Ces lignes s'appellent hashbang, Shebang et un certain nombre d'autres noms similaires. Voir ceci entrée FOLDOC , ceci article Wikipedia et Est-ce que #!/Bin/sh est lu par l'interprète? pour plus d'informations.

  • Si un fichier texte est marqué comme exécutable et que vous l'exécutez à partir de votre shell (comme ./filename) mais il ne le fait pas commencez par #!, le noyau ne parvient pas à l'exécuter. Cependant, voyant que cela est arrivé, votre shell va essayer l'exécuter en passant son nom à n pe Shell. Il y a peu d'exigences placé sur quoi Shell qui est ("le Shell exécutera une commande équivalente à l'invocation d'un Shell ...") . En pratique , quelques shells - dont bashname__ * - exécuter une autre instance d'eux-mêmes, tandis que d'autres utilisent /bin/sh. Je vous recommande vivement d'éviter cela et d'utiliser plutôt une ligne hashbang (ou exécutez le script en le transmettant à l'interpréteur souhaité, par exemple, bash filename).

    * Manuel GNU Bash , .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, c’est supposé être un script shell et le shell l'exécute comme décrit dans scripts shell . "

67
Eliah Kagan

Oui, vous manquez 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. Avoir un sens?

Alors essayez ceci:

MY_VAR=12345 
. ./testenv.sh
13
user1477