# One Script, Several Files: source

A library is a file of functions that several scripts share: each script loads it with source, and finds it from any folder with here=$(dirname "${BASH_SOURCE\[0\]}"), the folder the script itself is saved in.

Journeyman · Unix · Linux · Updated November 10, 2026

## After this lesson you can

- Share functions between scripts by keeping them in a library file and loading it with source
- Find a script's own folder with dirname and ${BASH_SOURCE\[0\]}, so it can load its library from any folder

## Key takeaways

- A library is a file of functions only. Scripts load it with source, so a fix made in the library reaches every script that uses it.
- A library is never run on its own, so it needs no #! line and no chmod +x.
- source mix-lib.sh looks in the folder you're in, not the script's folder, so the script breaks when it's run from anywhere else.
- here=$(dirname "${BASH_SOURCE[0]}") keeps the folder the script is saved in, so source "$here/mix-lib.sh" works from any folder.

The shuffler is turning into two scripts: `mix.sh` prints the mix, and a new `stats.sh` will say how long it is. Both need `banner`, and the stats need `sum_minutes` too. Copying the functions into both works for a week, until you fix a bug in one copy and the other keeps it. A garage has a better habit: one shared toolbox, and every project borrows the hammer from it instead of buying its own. In a script, the shared toolbox is a **library**, a file of functions any script can load. It's how bigger tools stay organized: `spend` will keep its functions in a library, and so do the scripts that set up servers and deploy websites at work.

## A library: a file of functions

In the shuffler folder from *Functions: Naming a Job* (`mkdir -p ~/terminal-practice/shuffler && cd ~/terminal-practice/shuffler` makes it), write `mix-lib.sh` with nano. It holds `banner` and the `sum_minutes` from *Local Variables and Getting a Value Back*:

```bash
# mix-lib.sh: functions for the playlist scripts.
# Not run on its own: a script loads it with source.

banner() {
  echo "------------------------------"
  echo "  $1"
  echo "------------------------------"
}

sum_minutes() {
  local total=0 m
  for m in "$@"; do
    total=$((total + m))
  done
  echo "$total"
}
```

A library has no `#!` line and needs no `chmod +x`, because nobody runs it: scripts load it. Keep it to definitions. A library that also did something, such as printing a line or deleting a file, would do it in every script that loads it.

## source: load the library into a script

Write `stats.sh`, and make it runnable with `chmod +x stats.sh`:

```bash
#!/usr/bin/env bash
# stats.sh: how long is the road-trip mix?
source mix-lib.sh

banner "Mix stats"
echo "Total: $(sum_minutes 4 3 5 4 6 3) minutes"
```

```bash
./stats.sh
```

```text
------------------------------
  Mix stats
------------------------------
Total: 25 minutes
```

`source` is the same command you used in *Startup Files: .bashrc, .zshrc and source*: it reads a file into the shell that's running, here the script's own, so the functions it defines stay. `.` (read "dot") is its short name: `. mix-lib.sh` does the same.

## Run it from somewhere else, and it breaks

Now run the script from your home folder, by its full path:

```bash
cd ~
~/terminal-practice/shuffler/stats.sh
```

```text
/home/sam/terminal-practice/shuffler/stats.sh: line 3: mix-lib.sh: No such file or directory
/home/sam/terminal-practice/shuffler/stats.sh: line 5: banner: command not found
/home/sam/terminal-practice/shuffler/stats.sh: line 6: sum_minutes: command not found
Total:  minutes
```

`source mix-lib.sh` looked for the library in the folder you're in, your home folder. It's the same trap as `cat todo.txt` in *Run It from Anywhere: ~/bin on Your PATH*. A full path such as `~/terminal-practice/shuffler/mix-lib.sh` would fix it until you move the folder or give it to a friend. The script needs to know where *it* lives.

## Where the script lives: ${BASH_SOURCE\[0\]}

`BASH_SOURCE` is a variable bash fills in with the path of the file it's reading. It holds a list, and `[0]` picks the first item, the file running now (the next topic, *Arrays*, explains lists like it). `dirname` cuts the file's name off a path and leaves its folder. Write `where.sh`, make it runnable, and run it two ways from your home folder:

```bash
#!/usr/bin/env bash
# where.sh: where does this script live?
echo "This file: ${BASH_SOURCE[0]}"
echo "Its folder: $(dirname "${BASH_SOURCE[0]}")"
```

```bash
cd ~
terminal-practice/shuffler/where.sh
~/terminal-practice/shuffler/where.sh
```

```text
This file: terminal-practice/shuffler/where.sh
Its folder: terminal-practice/shuffler
This file: /home/sam/terminal-practice/shuffler/where.sh
Its folder: /home/sam/terminal-practice/shuffler
```

However you start the script, the folder is right. So change the top of `stats.sh` to keep it in `here`, and load the library from there:

```bash
here=$(dirname "${BASH_SOURCE[0]}")
source "$here/mix-lib.sh"
```

```bash
cd /tmp
~/terminal-practice/shuffler/stats.sh
```

```text
------------------------------
  Mix stats
------------------------------
Total: 25 minutes
```

One case still trips it: a link made with `ln -s` in `~/bin` (*Shortcuts to Files: ln -s*). Then `${BASH_SOURCE[0]}` is the link's path, and `here` is `~/bin`. `readlink -f` follows a link to the real file first, so the line becomes `here=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")`. On a Mac, `readlink -f` needs macOS 12.3 or newer.

`BASH_SOURCE` is bash's own; zsh doesn't have it, which is one more reason every script in this course starts with `#!/usr/bin/env bash`.

## Check yourself

2 questions to try on the lesson page.

## So what

A library and two lines at the top of each script let several tools share one set of functions and run from any folder, which is how a pile of scripts grows into an application.

## Key terms

- [source](https://www.ask-codey.com/glossary/source-unix)
- [Library](https://www.ask-codey.com/glossary/library)
- [BASH_SOURCE](https://www.ask-codey.com/glossary/bash-source)
- [dirname](https://www.ask-codey.com/glossary/dirname)
- [Shell Function](https://www.ask-codey.com/glossary/shell-function)

## Cheat sheets

- [One Script, Several Files: source](https://www.ask-codey.com/cheat-sheets/bash-script-libraries-cheat-sheet)

## Related

- [Startup Files: .bashrc, .zshrc and source](https://www.ask-codey.com/lessons/bash-startup-files)
- [Functions: Naming a Job](https://www.ask-codey.com/lessons/bash-functions)
- [Local Variables and Getting a Value Back](https://www.ask-codey.com/lessons/bash-function-return-values)
- [Run It from Anywhere: ~/bin on Your PATH](https://www.ask-codey.com/lessons/bash-scripts-on-your-path)

Source: https://www.ask-codey.com/lessons/bash-script-libraries
