web-dev-qa-db-fra.com

Comment puis-je ajouter un répertoire à PATH dans un script afin qu'il affecte le shell appelant et le reste de la session?

Je suis nouveau sur Ubuntu.

J'ai écrit un script pour ajouter un répertoire à l'environnement PATH. Lorsque je lance le script, il fonctionne correctement et le répertoire est ajouté à PATH. Mais il semble que le changement ne dure que jusqu'à la fin du script au lieu de durer toute la session. Quand je regarde le PATH après l'exécution du script, le répertoire n'est plus là. Aucune suggestion?

2
JP JP

Il y a deux choses à retenir:

  1. Les commandes, y compris les scripts, conservent leur environnement pendant toute la durée de l'exécution de la commande.

  2. Les commandes héritent de l'environnement du processus parent. Pour les commandes lancées via Shell, elles hériteront du shell.

Donc si vous faites PATH=$PATH:/my/dir cela ne durera que pour la durée des scripts. Pour le rendre permanent, le Shell parent doit être conscient du changement. La meilleure façon de le faire serait d'écrire sur ~/.bashrc si vous utilisez un fichier rc bash ou approprié pour votre shell. Ainsi, nous pouvons utiliser >> pour ajouter au fichier

echo PATH=$PATH:/my/dir >> ~/.bashrc

Et quand le script se termine, lancez

source ~/.bashrc

afin que le shell relise la configuration et soit au courant des modifications. Désormais, chaque commande exécutée dans Shell et chaque nouveau shell interactif hériteront de la nouvelle variable PATH.

Les deux étapes peuvent être réunies dans une fonction car (du moins pour bash), les fonctions sont exécutées dans l’environnement Shell actuel. Par conséquent, contrairement à un script qui exécute une partie source, appeler source à partir d’une fonction affecte le Shell actuel.

2

La réponse de Sergiy Kolodyazhnyy identifie le problème ici: les scripts sont exécutés dans un shell séparé du shell qui les appelle, et les commandes qui affectent le shell lui-même, telles que l'attribution de variables ou la modification du répertoire de travail, ne le sont pas. affecte le shell appelant, seul le shell enfant et (s’ils export variables à l’environnement) ses enfants.

Vous pouvez jouer avec cela en utilisant les variables humbles de Shell ...

$ foo=bar
$ echo $foo
bar
$ echo -e "foo=baz \n"'echo $foo' > script
$ cat script
foo=baz 
echo $foo
$ bash script
baz
$ echo $foo
bar

Bien que, comme le montre Eliah Kagan dans cette réponse , il est plus facile de le faire avec des sous-shell.

J'écris cette réponse au cas où vous ne voudriez pas ajouter en permanence le répertoire à votre PATH, mais uniquement à la session Shell en cours.

Pour ce faire, vous devez simplement exécuter le script dans le shell actuel . Ceci est fait avec la commande source qui est abrégée en . (point).

Compte tenu de cette version légèrement simplifiée de votre script ...

read -rp "What did you want to add to PATH? "
[ -d "$REPLY" ] && 
PATH="$PATH:$(readlink -m $REPLY)" &&
echo "OK, adding $REPLY to PATH" &&
echo "$PATH" ||
echo "seems like $REPLY is not a directory"

Notez que je reçois le même résultat que vous lorsque je lance le script de la manière habituelle:

$ ./add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Mais lorsque je source le script, il fonctionne comme prévu:

$ . add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground

Je vais ajouter trois apartés:

  • Je recommande d'ajouter des attributions PATH à ~/.profile plutôt qu'à ~/.bashrc, car ~/.bashrc provient de chaque Bash Shell interactif, y compris des shells démarrés à partir du shell actuel - cela signifie que des shells enfants pourraient se retrouver très longtemps PATHs, car ils héritent de PATH et s’y ajoutent lorsqu’ils sources ~/.bashrc. En revanche, ~/.profile ne provient généralement que de la connexion (ou des shells de connexion).
  • Vous n'avez pas besoin de export lorsque vous affectez à PATH car il s'agit déjà d'une variable d'environnement: dans un sens, elle est déjà exportée et le restera: une affectation à PATH sera toujours héritée par les processus enfants (mais pas par les processus parents, comme vous avez découvert!) sans être explicitement exported.
  • J'ai cité les variables REPLY et PATH dans l'ensemble. C'est une bonne idée car il peut y avoir des espaces ou d'autres caractères qui déclenchent des extensions du shell. Cependant, un effet secondaire de ceci est que ~ n'est pas développé, le script est donc susceptible de renvoyer des éléments tels que

    looks like ~/some-existing-dir is not a directory
    

    ce qui est vrai (en prenant ~ littéralement) mais pas très utile. Peut-être que le script devrait en avertir l'utilisateur ...

1
Zanna