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 export
ed.
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...