funwithlinux blog

Shell Script Current Directory: What It Is & How It Differs from Your Calling Location

Shell scripts are the workhorses of Unix/Linux automation, enabling users to chain commands, automate repetitive tasks, and build complex workflows. Yet, one of the most common sources of frustration and bugs in shell scripting stems from confusion around directories. Specifically:

  • Where is the shell script stored on the filesystem? (We’ll call this the script directory.)
  • From which directory was the shell script executed? (We’ll call this the calling location or working directory.)

These two concepts are often conflated, leading to broken paths, missing files, and scripts that work in one scenario but fail in another. In this blog, we’ll demystify these terms, explore their critical differences, and provide actionable guidance to avoid pitfalls. By the end, you’ll confidently reference files, call subscripts, and manage paths in your shell scripts—no matter where they’re run from.

2026-01

Table of Contents#

  1. Understanding the Basics
  2. Defining Key Terms
  3. How Are These Directories Determined?
  4. Key Differences: Script Directory vs. Calling Location
  5. Practical Examples to Illustrate the Difference
  6. Common Pitfalls and How to Avoid Them
  7. How to Get the Script’s Directory Reliably
  8. Summary
  9. References

Understanding the Basics#

Before diving into the differences, let’s ground ourselves in foundational concepts.

What is a Shell Script?#

A shell script is a plain-text file containing a sequence of commands interpreted by a Unix/Linux shell (e.g., bash, zsh, or sh). When executed, the shell reads and runs these commands sequentially, automating tasks like file backups, log processing, or system configuration.

What is a Directory in Unix/Linux?#

A directory (or "folder") is a filesystem container that holds files and other directories. Directories are organized in a hierarchical tree structure, rooted at / (the root directory).

  • Absolute Path: A full path starting from the root directory (e.g., /home/user/scripts). Always uniquely identifies a location.
  • Relative Path: A path relative to the current working directory (e.g., ./scripts or ../docs). Depends on where you’re currently located in the filesystem.

The pwd command (short for "print working directory") shows your current working directory in the shell.

Defining Key Terms#

To avoid confusion, let’s formally define the two critical directories we’ll discuss:

What is the "Script Directory"?#

The script directory is the absolute path to the directory where the shell script file itself is stored on the filesystem. For example:

  • If your script is saved as /home/alice/projects/backup.sh, the script directory is /home/alice/projects.

This directory is fixed unless you move the script file. It is not affected by where or how the script is executed.

What is the "Calling Location"?#

The calling location (or working directory) is the directory from which the shell script is executed. It is the directory you were in when you ran the script command. For example:

  • If you run ./backup.sh from /home/alice, the calling location is /home/alice.
  • If you run /home/alice/projects/backup.sh from /tmp, the calling location is /tmp.

This directory is dynamic: it changes based on where the user runs the script.

How Are These Directories Determined?#

Script Directory: Fixed at Creation#

The script directory is determined when you save the script file. It is a property of the script’s location on the filesystem, not how it’s executed. To find it, you need to resolve the path to the script file itself (e.g., using $0 in bash, which holds the script’s name or path).

Calling Location: Dynamic at Execution#

The calling location is inherited from the parent shell. When you run a script, the new shell process (or subshell) that executes the script uses the parent shell’s current working directory as its own. This means:

  • If you run cd /tmp and then ~/scripts/myscript.sh, the script’s working directory (calling location) is /tmp.
  • If you run the same script from ~/documents, the calling location is ~/documents.

You can check the calling location from within the script using the pwd command.

Key Differences: Script Directory vs. Calling Location#

To solidify the distinction, let’s compare these two directories side-by-side:

FeatureScript DirectoryCalling Location
DefinitionWhere the script file is stored.Where the user ran the script from.
Fixed/DynamicFixed (unless the script is moved).Dynamic (changes based on execution context).
Affected byThe script’s filesystem path.The user’s location when running the script.
How to RetrieveUse dirname "$0" (with resolving for absolute paths).Use pwd in the script.
ExampleFor /opt/tools/cleanup.sh, it’s /opt/tools.If run from ~, it’s /home/user.

Practical Examples to Illustrate the Difference#

Let’s walk through concrete examples to see these directories in action.

Example 1: Basic Directory Reporting#

Create a script called dir_demo.sh and save it in /home/user/scripts/ with the following content:

#!/bin/bash
 
# Print the calling location (working directory)
echo "Calling Location (Working Directory): $(pwd)"
 
# Print the script directory (relative path)
echo "Script Directory (Relative): $(dirname "$0")"
 
# Print the script directory (absolute path, resolved)
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
echo "Script Directory (Absolute): $SCRIPT_DIR"

Make it executable:

chmod +x /home/user/scripts/dir_demo.sh

Now, run the script from different calling locations and observe the output.

Scenario 1: Run from the script directory itself#

cd /home/user/scripts
./dir_demo.sh

Output:

Calling Location (Working Directory): /home/user/scripts
Script Directory (Relative): .
Script Directory (Absolute): /home/user/scripts

Here, the calling location matches the script directory, so pwd and SCRIPT_DIR are the same.

Scenario 2: Run from the user’s home directory#

cd ~  # Equivalent to /home/user
scripts/dir_demo.sh

Output:

Calling Location (Working Directory): /home/user
Script Directory (Relative): scripts
Script Directory (Absolute): /home/user/scripts

Now the calling location is /home/user, but the script directory remains /home/user/scripts.

Scenario 3: Run from a completely different directory#

cd /tmp
/home/user/scripts/dir_demo.sh

Output:

Calling Location (Working Directory): /tmp
Script Directory (Relative): /home/user/scripts
Script Directory (Absolute): /home/user/scripts

The calling location is /tmp, but the script directory is still /home/user/scripts.

Example 2: The Perils of Relative Paths#

A common pitfall is assuming the script runs from its own directory, leading to broken relative paths. Let’s see why this happens.

Suppose you have a script process_data.sh in /home/user/analytics/ that needs to read a data file input.csv stored in the same directory:

#!/bin/bash
 
# Bad practice: Using a relative path without anchoring to the script directory
cat ./input.csv

If you run the script from its own directory (/home/user/analytics), it works:

cd /home/user/analytics
./process_data.sh  # Reads /home/user/analytics/input.csv → success!

But if you run it from your home directory:

cd ~
analytics/process_data.sh  # Tries to read /home/user/input.csv → ERROR: No such file!

The script fails because ./input.csv is relative to the calling location (/home/user), not the script directory (/home/user/analytics).

Common Pitfalls and How to Avoid Them#

Pitfall 1: Assuming the Script Runs from Its Own Directory#

Problem: Using relative paths like ./data.txt or subscript.sh assumes the script is run from its own directory. If run elsewhere, these paths break.

Solution: Always reference files relative to the script directory, not the calling location. Use the script directory to construct absolute paths.

Pitfall 2: Relying on $0 Without Resolving Relative Paths#

Problem: The $0 variable holds the script’s name or path, but it may return a relative path (e.g., ./dir_demo.sh or scripts/dir_demo.sh), not an absolute path.

Solution: Resolve $0 to an absolute path using cd and pwd (as shown in Example 1) or tools like readlink/realpath to handle symlinks.

Problem: If the script is executed via a symlink (e.g., ln -s /opt/scripts/real_script.sh ~/bin/script), $0 may point to the symlink path, not the actual script directory.

Solution: Use readlink -f "$0" to resolve symlinks and get the absolute path to the real script file.

How to Get the Script’s Directory Reliably#

To avoid the pitfalls above, you need a reliable way to get the script directory as an absolute path. Here’s the gold-standard method for bash scripts:

The Gold-Standard One-Liner#

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

Let’s break this down:

  • ${BASH_SOURCE[0]}: A bash-specific variable that holds the path to the current script (more reliable than $0 in subshells).
  • dirname "${BASH_SOURCE[0]}": Extracts the directory path of the script (may be relative).
  • cd ... &>/dev/null: Changes the working directory to the script’s directory (resolving relative paths). The &>/dev/null suppresses errors (e.g., if the directory doesn’t exist, though this is rare for existing scripts).
  • && pwd: Prints the absolute path of the resolved directory (only if cd succeeds).

To resolve symlinks and get the path to the actual script (not the symlink), use readlink -f (GNU) or realpath (BSD/macOS):

# For GNU systems (Linux)
SCRIPT_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
 
# For BSD/macOS (requires coreutils; install with `brew install coreutils`)
SCRIPT_DIR=$(dirname "$(greadlink -f "${BASH_SOURCE[0]}")")

Example: Fixing the Broken process_data.sh Script#

Using the reliable script directory method, we can fix the process_data.sh example from earlier:

#!/bin/bash
 
# Get the script directory as an absolute path
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
 
# Good practice: Reference files relative to the script directory
cat "$SCRIPT_DIR/input.csv"

Now, no matter where you run the script from, it will correctly read /home/user/analytics/input.csv.

Summary#

Understanding the difference between the script directory (where the script is stored) and the calling location (where it’s executed) is critical for writing robust shell scripts. Key takeaways:

  • Script Directory: Fixed, absolute path to the script’s storage location. Retrieve it with SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd).
  • Calling Location: Dynamic, inherited from the parent shell. Check it with pwd in the script.
  • Avoid Pitfalls: Never use relative paths without anchoring them to the script directory. Resolve symlinks and relative paths to ensure reliability.

By mastering these concepts, you’ll write scripts that work consistently—whether run from your home directory, a server’s /tmp folder, or via a symlink.

References#