How to Get the Directory of a Bash Script
--
Have you ever tried to get the directory of your Bash script programmatically?
It’s a common thing people ask and it can be done in different ways.
The first thing to look at to solve this problem is the $0 variable, used in Bash to store the first element of the command executed.
Create a script called get_script_dir.sh
in the directory /opt/scripts/:
#!/bin/bashecho "$0"
If we execute it:
- From the script directory (/opt/scripts/)
- Using the relative path from the parent directory /opt
- Using the absolute path of the script
We get the following:
1) From script directory: /opt/scripts
[ec2-user@ip-172-12-20-120 ~]$ ./test.sh
./test.sh2) Relative path from parent directory: /opt
[ec2-user@ip-172-12-20-120 opt]$ scripts/get_script_dir.sh scripts/get_script_dir.sh3) Absolute path
[ec2-user@ip-172-12-20-120 ~]$ /opt/scripts/get_script_dir.sh /opt/scripts/get_script_dir.sh
So this is not enough because only in the scenario 3) we get the full path for the script.
First of all, to find a solution we need to introduce the dirname
command that strips the last component from a file name. We update our script to add dirname $0
:
#!/bin/bashecho "$0"
dirname "$0"
And here is the output of the script in the three scenarios shown before:
1) From script directory: /opt/scripts
[ec2-user@ip-172-12-20-120 scripts]$ ./get_script_dir.sh ./get_script_dir.sh
.2) Relative path from parent directory: /opt
[ec2-user@ip-172-12-20-120 scripts]$ cd ..
[ec2-user@ip-172-12-20-120 opt]$ scripts/get_script_dir.sh scripts/get_script_dir.sh
scripts3) Absolute path
[ec2-user@ip-172-12-20-120 opt]$ /opt/scripts/get_script_dir.sh /opt/scripts/get_script_dir.sh
/opt/scripts
So the dirname allows, in scenario 3), to get the directory of the script.
Obviously this approach doesn’t work well, we want a command that gives back the full path of the script when it’s executed from any directory (script directory, relative path and absolute path).
Let’s also test how $0 behaves with the source
command, very common in Bash scripting to execute the lines in a script:
[ec2-user@ip-172-12-20-120 scripts]$ source get_script_dir.sh
-bash
This time we don’t get the path of the script back but just -bash
.
This doesn’t work in the way we want, we need to find an alternative.
The BASH_SOURCE array
The $BASH_SOURCE array represents an alternative to $0 that is a lot more robust.
I don’t want to get into too many details about $BASH_SOURCE that can be quite confusing at this stage.
The only thing that matters right now is that $BASH_SOURCE always contains the name and path of the script executed. As we have seen before this is true for $0 only when the script is not sourced (using the source command).
With $BASH_SOURCE our script becomes:
#!/bin/bashecho "${BASH_SOURCE[0]}"
dirname "${BASH_SOURCE[0]}"
Where ${BASH_SOURCE[0]}
is the first element of the BASH_SOURCE array.
Let’s see how it behaves in the three scenarios we have analysed before:
1) From script directory: /opt/scripts
[ec2-user@ip-172-12-20-120 scripts]$ ./get_script_dir.sh ./get_script_dir.sh
.2) Relative path from parent directory: /opt
[ec2-user@ip-172-12-20-120 scripts]$ cd ..
[ec2-user@ip-172-12-20-120 opt]$ scripts/get_script_dir.sh
scripts/get_script_dir.sh
scripts3) Absolute path
[ec2-user@ip-172-12-20-120 opt]$ /opt/scripts/get_script_dir.sh /opt/scripts/get_script_dir.sh
/opt/scripts
So there are no changes compared to $0…so what’s the point of using BASH_SOURCE?
What happens if we source our script in the same way we have done before?
[ec2-user@ip-172-12-20-120 scripts]$ source get_script_dir.sh get_script_dir.sh
.
That’s great!
This time we get the correct output instead of -bash
(see example in the previous section using source and $0).
A One Liner to Get the Script Directory
Now that we know that it’s better to use the $BASH_SOURCE variable we want to come up with a generic way to get the directory of the script…
…no matter the location the script is executed from.
We can use the following approach:
- Get the script directory (relative to the current directory)
- cd into the directory
- Use pwd to get the absolute path
A script that follows the three steps above would look like:
#!/bin/bash# Step 1
SCRIPT_RELATIVE_DIR=$(dirname "${BASH_SOURCE[0]}")# Step 2
cd $SCRIPT_RELATIVE_DIR# Step 3
pwd
And now let’s go through the three scenarios we have seen before:
1) From script directory: /opt/scripts
[ec2-user@ip-172-12-20-120 scripts]$ ./get_script_dir.sh /opt/scripts2) Relative path from parent directory: /opt
[ec2-user@ip-172-12-20-120 scripts]$ cd ..
[ec2-user@ip-172-12-20-120 opt]$ scripts/get_script_dir.sh /opt/scripts3) Absolute path
[ec2-user@ip-172-12-20-120 opt]$ /opt/scripts/get_script_dir.sh /opt/scripts
Finally something that works well…
It doesn’t work if every scenario (e.g. with symlinks) but it’s enough to cover most use cases.
Now we want to create a one liner to put the three Steps together:
Step 1 + Step 2
cd $(dirname "${BASH_SOURCE[0]}")Step 1 + Step 2 + Step 3
cd $(dirname "${BASH_SOURCE[0]}") && pwd
And to store this directory into a variable called SCRIPT_DIR we use:
SCRIPT_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
The pwd command is executed only if cd $(dirname "${BASH_SOURCE[0]}")
is successful.
How do we do that? Using &&.
Then you can use the echo command to print the value of $SCRIPT_DIR.
Conclusion
I thought getting the directory of a Bash script would be super simple!
As we have seen in this article it’s not the case…
Now you know how to use $0 and $BASH_SOURCE.
Also how to run a command if the execution of the previous command is successful using &&.
I hope you have found this useful…
How will you use this knowledge?
Are you writing a Bash script right now in which you need to get the directory of your script?
Let me know! 😀
Originally published at https://codefather.tech on March 5, 2020.