100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Sourcing Scripts and Libraries

Learn how `source`/`.` works, how it differs from executing a script, and how to structure reusable Bash libraries that are safe to source.

Functions & ScopeIntermediate8 min readJul 10, 2026
Analogies

What Sourcing Actually Does

Running source file.sh (or the POSIX-equivalent . file.sh) reads and executes the file's contents inside the current shell process, rather than launching a new subshell the way ./file.sh would. This means every variable, function, and even cd performed inside the sourced file directly affects the calling shell's environment — which is exactly the mechanism that lets a small lib.sh of shared functions become available to any script that sources it, but also why a careless sourced script can silently change the caller's working directory or overwrite its variables.

🏏

Cricket analogy: A substitute fielder brought onto the field mid-over directly joins the existing team formation rather than starting a separate match — that's sourcing versus running a script as its own subshell.

Source vs Execute: A Concrete Comparison

./setup.sh forks a new process, runs the script there, and any cd or exported variable inside it disappears when that process exits — the calling shell is unaffected. source setup.sh runs the exact same commands in the calling shell's own process, so a cd /tmp inside it leaves the caller sitting in /tmp afterward, and any exit inside it terminates the caller's shell entirely, not just the sourced script. This is precisely why interactive shell configuration files like .bashrc and .bash_profile must be sourced, not executed — their whole purpose is to modify the current shell's environment.

🏏

Cricket analogy: Watching a match highlights reel (execute, isolated) versus actually walking onto the pitch and joining the live match (source, shared state) — one affects only itself, the other affects the real game in progress.

bash
#!/usr/bin/env bash
set -euo pipefail

# lib/strings.sh — designed to be sourced, not executed
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    echo "strings.sh is a library; source it, don't execute it directly." >&2
    exit 1
fi

strings::trim() {
    local s="$1"
    s="${s#"${s%%[![:space:]]*}"}"   # trim leading whitespace
    s="${s%"${s##*[![:space:]]}"}"   # trim trailing whitespace
    printf '%s' "$s"
}

# main.sh — the consumer script
# source "$(dirname "${BASH_SOURCE[0]}")/lib/strings.sh"
# trimmed=$(strings::trim "   hello world   ")
# echo "[$trimmed]"

Guarding Against Double-Sourcing and Unsafe Paths

If two different library files both source a common utils.sh, or a script sources the same file twice through different relative paths, functions get redefined harmlessly but constant readonly declarations will throw an error on the second attempt. Guard against this with an include-once pattern: check and set a uniquely-named flag variable (e.g. [[ -n "${_UTILS_SH_INCLUDED:-}" ]] && return; _UTILS_SH_INCLUDED=1) at the top of the library file. Also always resolve the library's own path relative to ${BASH_SOURCE[0]}, not $0 or a hardcoded path, so it works correctly regardless of which script sources it or from what working directory.

🏏

Cricket analogy: A player who has already been declared 'retired out' can't be re-added to the batting order a second time without the scorer flagging a clear rule violation — an include-once guard prevents exactly this kind of duplicate registration.

Resolve a sourced library's own directory reliably with: LIB_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd). Using ${BASH_SOURCE[0]} instead of $0 is essential here — $0 refers to the top-level script that was invoked, not the library file currently being sourced, so it would resolve to the wrong directory.

Never call exit inside a file meant to be sourced. Because sourcing runs in the caller's own shell process, exit terminates that entire shell — including an interactive terminal session if a user accidentally sources a library file directly. Use return for early exits from a sourced file instead.

  • source file.sh (or . file.sh) runs the file in the current shell process; ./file.sh runs it in a new subshell.
  • Only sourcing lets a script's variables, functions, and cd calls persist in the calling shell.
  • .bashrc/.bash_profile must be sourced, not executed, because their purpose is to modify the current shell.
  • Guard against double-sourcing with an include-once flag variable checked at the top of the file.
  • Resolve a library's own path with ${BASH_SOURCE[0]}, never $0 or a hardcoded absolute path.
  • Never call exit inside a sourced file — use return to avoid killing the caller's shell.
  • A library file can detect direct execution vs sourcing by comparing ${BASH_SOURCE[0]} to $0.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#SourcingScriptsAndLibraries#Sourcing#Scripts#Libraries#Actually#StudyNotes#SkillVeris