Get the Current Script Directory in Bash

Last updated: June 26, 2023 | Aditya Harsh

1. Introduction

In Bash scripting, it's often useful to determine the directory in which a script is located. This information can be helpful for various tasks, such as accessing other files relative to the script or performing operations within the script's directory.

In this guide, we focus our attention on the two major special variables- $0 and BASH_SOURCE and combine them with the existing tools to get the current script's directory.

2. Why not pwd?

When running any Bash script, we effectively execute it in a subshell. It runs in a separate environment. This shell differs from the shell that we are using to run the script. The pwd command gives us the current working directory of the subshell rather than the directory containing the Bash script.

We can understand this with an example. 

Create a script named dirScript1.sh in the directory /home/ubuntu/Downloads and add:

#!/bin/bash 
pwd

We execute the above script from /home/ubuntu/Desktop:

sh /home/ubuntu/Downloads/dirScript1.sh

When we run, the script generates the output which will be /home/ubuntu/Desktop. However, our script is located in /home/ubuntu/Downloads. Hence, we should be careful when dealing with the pwd command. It will return us the current working directory and not the directory of the Bash script. 

3. What is $0?

We often encounter special variables in Bash scripts. $0 is one such variable containing the name of the current script that is under execution. It is also termed as the zeroth argument.

When a script begins its execution, Bash informs the interpreter. It sends the name of that script as the first argument. Then the $0 variable is set to the name of the script. It can include the path, if specified. 

Let us try an example. We have a Bash script - dirScript2.sh located in the "/home/ubuntu/Downloads" directory with the below contents:

#!/bin/bash 
echo $0

We execute the script from the same location with the absolute path as follows:

sh /home/ubuntu/Downloads/dirScript2.sh

Our scripting is simply echoing $0. It has the value /home/ubuntu/Downloads/dirScript2.sh. To extract the directory path from this, we can use the dirname command.

4. Bash get the script directory

We have to follow a step-by-step process to determine the script directory.  We will understand this process in detail and learn where it can fail. We will also learn how to resolve the symbolic link, if present.

4.1 BASH_SOURCE variable

Another special Bash variable is the BASH_SOURCE. It is an array that stores the source file names corresponding to the executing script. 

The first element of the array will be BASH_SOURCE[0]. It has the name of the current script. This will contain the same contents as $0, we saw above. 

The BASH_SOURCE variable is useful when scripts are calling other scripts. We can determine the source of the current execution context by examining this array.

Example:

#!/bin/bash
echo "My current script is: ${BASH_SOURCE[0]}"
echo "My current script is: ${BASH_SOURCE}"
echo "My parent is: ${BASH_SOURCE[1]}"
echo "My grandparent is: ${BASH_SOURCE[2]}"
echo "My great grandparent is: ${BASH_SOURCE[3]}"

The different indices of the BASH_SOURCE array variable contain the current script, the parent script, and so on. If the script is not invoked from any other script, the values of BASH_SOURCE[1], BASH_SOURCE[2], and BASH_SOURCE[3] will remain empty.

4.2 Dirname command

We have already learnt different ways to extract the name of the directory. A simple and effective way is to use the Bash built-in utility named dirname. 

Example:

dirname /home/ubuntu/sample/dirA/A_file.txt

The dirname command first looks for the last path separator (/). It then displays whatever is present before that. It omits everything after that. 

4.3 Putting everything together so far

Now that we are armed with the understanding of the $0 variable, the BASH_SOURCE variable and the dirname command, we can put everything into practice to get the source directory of any Bash script. 

Example:

#!/bin/bash
echo 'Script using $0 is: ' $0
echo 'Script using BASH_SOURCE is: ' ${BASH_SOURCE}

mydir=$(dirname "$0")
echo 'Directory of the script: ' $mydir

Now let us run this script using the command:

sh /home/ubuntu/Downloads/dirScript4.sh

Using the $0 variable, we fetch the full path to the script. We also do the same using the other special array variable BASH_SOURCE.  We then supply this to the dirname command. It helps to retrieve the directory portion from the path, which we store in the mydir variable. We finally print its contents.

4.4 Readlink command

The readlink command is a great tool in our armory to resolve symbolic links and get the desired path. It accepts one or more arguments. These arguments are the paths of symbolic links to be resolved.

It can be incorporated in Bash scripts for determining the directory path of the script. This is vital in scenarios where the script is invoked through a symbolic link or a relative path.

Upon executing any Bash script, the $0 variable will store the name of the script. It may not contain the entire absolute path to the script. Furthermore, if our script gets invoked through a symbolic link or a relative path, the $0 variable will contain the path to the link or the relative path.

This is where the readlink command comes to our rescue. It resolves any symbolic links and obtains the absolute path to the script.

Let us understand this with an example:

#!/bin/bash

mydir="$(dirname "$0")"
echo "The script directory without readlink is: $mydir"

mydir="$(dirname "$(readlink -f "$0")")"
echo "The script directory with readlink is: $mydir"

We run this script as:

./dirScript5.sh

The special parameter $0 contains the name of the file which is ./dirScript4.sh

In the first scenario we use the dirname command on $0 directly. This will simply trim everything after the path separator (/) and give us simply the dot ".". It will not expand the value of this dot.

In the next scenario, we employ the readlink command on $0 first. This helps obtain the absolute path of the script. During this process, it will also handle any symbolic links. Lastly, we bring the dirname into the picture to fetch the directory portion. We store this in the mydir variable and display its value. 

The readlink command always ensures that we obtain the correct directory path of the script, regardless of how it is invoked. 

4.5 Links

The BASH_SOURCE or the $0 variable may contain the path to the link and not the actual script path. For this we have to handle the link recursively and resolve it using the readlink command we saw above.

Example:

#!/bin/bash

scriptPath="${0}"
#scriptPath="${BASH_SOURCE}"

while [ -L "${scriptPath}" ]
do
    path="$(readlink "${scriptPath}")"

    if [[ "${path}" == /* ]]
    then
        scriptPath="$path"
    else
        scriptPath="$(dirname "${scriptPath}")/${path}"
     fi

done

echo "scriptPath=${scriptPath}"

The variable scriptPath has the value of the currently executing script. We are using a while loop with the -L flag. This will run till this variable is a symbolic link. This loop helps to resolve any symbolic links in the path and obtain the absolute path to the script.

For every run of this loop, we have the readlink command to get the path of the symbolic link. If this path is an absolute path ("/"), our scriptPath becomes the value present in the path variable. If not, the dirname command extracts the directory part from the scriptPath. It appends the path variable to it. This results in the creation of the absolute path. 

This is an important aspect as symbolic links can also use relative paths. This needs to be resolved relative to the directory containing the link. After the loop ends, we print the scriptPath. It will hold the absolute path to the script. All symbolic links will be resolved. 

5. Summing up

Let us sum up the process of finding the directory path of the bash script into 3 simple steps:

  1. Use the $0 or the BASH_SOURCE special variable to get the script name with the path.
  2. Using the readlink command, obtain the absolute path and resolve symbolic links
  3. Use dirname command to extract the directory.
SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment