I recently needed to keep a Bash script in a centralized location, with many symbolic links to that script sprinkled throughout my repository. I wanted the script be able to determine both the user’s PWD, as well as the true location of the script. Based on all the different suggestions found online, I created a little test script to see how well each suggestion worked:
#!/bin/bash
echo "\$0"
echo $0
echo ""
echo "pwd -P"
pwd -P
echo ""
echo "pwd -L"
pwd -L
echo ""
echo "which \$0"
which $0
echo ""
echo "readlink -e \$0"
readlink -e $0
echo ""
echo "readlink -e \$BASH_SOURCE"
readlink -e $BASH_SOURCE
echo ""
I put this script (test.sh) in ~/ and then created a symlink to it in a different directory. Here are the results.
My friend JT left a comment below to say that using $BASH_SOURCE is probably the better choice than using $0, since $0 can be changed, and is only set equal to the file name by convention.
Directly calling the script from the same directory (/home/matthew/):
matthew@broderick:~$ ./test.sh $0 ./test.sh pwd -P /home/matthew pwd -L /home/matthew which $0 ./test.sh readlink -e $0 /home/matthew/test.sh
Directly calling the script from some other directory (/some/other/directory/):
matthew@broderick:~/some/other/directory$ ~/test.sh $0 /home/matthew/test.sh pwd -P /home/matthew/some/other/directory pwd -L /home/matthew/some/other/directory which $0 /home/matthew/test.sh readlink -e $0 /home/matthew/test.sh
Creating a symlink to ~/test.sh in ~/some/other/directory, and calling it directly (./test.sh):
matthew@broderick:~/some/other/directory$ ln -s ~/test.sh ./test.sh matthew@broderick:~/some/other/directory$ ./test.sh $0 ./test.sh pwd -P /home/matthew/some/other/directory pwd -L /home/matthew/some/other/directory which $0 ./test.sh readlink -e $0 /home/matthew/test.sh
Creating a symlink to ~/test.sh in ~/some/other/directory, and calling it from yet another location:
matthew@broderick:~/some/other/directory$ ln -s ~/test.sh ./test.sh matthew@broderick:~/some/other/directory$ cd ~/somewhere/else matthew@broderick:~/somewhere/else$ ~/some/other/directory/test.sh $0 /home/matthew/some/other/directory/test.sh pwd -P /home/matthew/somewhere/else pwd -L /home/matthew/somewhere/else which $0 /home/matthew/some/other/directory/test.sh readlink -e $0 /home/matthew/test.sh
Conclusion:
So, it looks like “readlink -e $0” will always return the full, non-symlink, “physical” location of the script (regardless of whether or not symlinks are involved). Looks like “pwd” returns the user’s current working directory reliably.
so, as you might imagine, $0 is argv[0], which means that it is set by the calling process’s exec* call.
*convention* is that argv[0] is the name of the script, but it’s not necessarily. you can easily change $0 of a script by calling it with execv or something differently. in fact, the way busybox works is it’s this massive single binary that chooses its behavior based on what argv[0] is.
what you’re probably most interested in is $BASH_SOURCE, which is the file bash loaded to run.
anyway, in practice it will never matter. argv[0] is hardly ever changed, so, probably doesn’t matter. hth
Thanks JT, that was very informative.
\o/