2

I am new to Ubuntu.

I wrote a script to add a dir to the PATH environment. When I run the script it runs fine and the dir is added to the PATH. But it seems that the change only lasts until the script exits instead of lasting for the length of the session. When I look at the PATH after the script is ran the dir is no longer there. Any suggestions?

2
  • There are no "Images below", only tags. Your question is clear and doesn't really need any screenshots to explain your situation further. However, FYI, information on inserting images into posts can be found at: meta.stackexchange.com/questions/75491/…
    – CentaurusA
    Sep 9, 2018 at 21:22
  • 4
    Please don't post photos of text. Copy the text here and apply code formatting: askubuntu.com/editing-help#code
    – muru
    Sep 10, 2018 at 8:30

2 Answers 2

3

There's two things to remember:

  1. Commands, including scripts, keep their environment for the duration of the command running

  2. Commands inherit environment from parent process. For commands started via shell, they'll inherit from the shell.

So if you do PATH=$PATH:/my/dir that'll last only for the scripts duration. To make it permanent, the parent shell needs to be aware of the change. Proper way to do that would be to write to ~/.bashrc if you're using bash or appropriate rc file for your shell. Thus we can use >> to append to file

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

And when the script exits, run

source ~/.bashrc

so that the shell rereads the config and will be aware of the changes. Now every command you run in shell and every new interactive shell started will inherit new PATH variable

The two steps can be put together into a function since ( at least for bash ) functions run in current shell environment, so unlike a script when you do source part, calling source from a function will affect the current shell.

1

Sergiy Kolodyazhnyy's answer identifies the problem here: scripts are executed in a separate child shell of the shell that calls them, and commands that affect the shell itself, such as assigning to variables or changing working directory, do not affect the calling shell at all, only the child shell and (if they export variables to the environment) its children.

You can play with this using humble shell variables...

$ 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

Although as Eliah Kagan shows in this answer it's easier to do so with subshells.

I am writing this answer in case you do not want to permanently add the directory to your PATH, but only to the current shell session.

To do this you simply need to run the script in the current shell. This is done with the source command which is abbreviated to . (dot).

Given this slightly simplified version of your 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"

Notice that I get the same result as you when I run the script in the usual way:

$ ./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

But when I source the script it works as expected:

$ . 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

I will add three asides:

  • I recommend adding PATH assignments to ~/.profile rather than ~/.bashrc because ~/.bashrc is sourced by every interactive Bash shell, including shells started from the current shell - this means child shells could end up with really long PATHs, as they inherit PATH as well as appending to it when they source ~/.bashrc. In contrast, ~/.profile is usually only sourced at login (or by login shells).
  • You don't need to export when you assign to PATH because it's already an environment variable: in a sense it is already exported and will remain so: an assignment to PATH will always be inherited by child processes (though not of parent processes, as you discovered!) without being explicitly exported.
  • I have quoted the variables REPLY and PATH throughout. This is a good idea because either may have spaces or other characters that trigger shell expansions. However, a side effect of this is that ~ is not expanded, so the script is apt to return things like

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

    which is true (taking ~ literally) but not very helpful. Maybe the script should warn the user of this...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.