What sourcery is this?
Tags: cli, beginners, shell
Published: 2020-06-14 12:53:28

---
title: What sourcery is this?
published: true
description: Why we "source" some shell scripts and not others
tags: cli, beginners, shell
series: Command-line myths
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/y9fmk7sq7vt6mj5gv37o.jpg
---

Sometimes you might hear people talk about _sourcing_ a shell script, and sometimes you might hear them talk about _running_ one, or _executing_ it.

So what's the difference? Aren't they the same thing?

I'm going to set up a little test script to show you what's going on under the rug. I'll call it `foo.sh`:

```sh
#!/bin/sh

new_path="/tmp/demo"

mkdir -p "$new_path" 
cd "$new_path"
printf "I am a script called %s...\n" "$0"
printf "My process ID is %d...\n" "$$"

printf "... and I have changed directory to %s\n" "$new_path"

printf "TESTVAR1 started off as '%s'\n" "$TESTVAR1"
printf "and TESTVAR2 started off as '%s'\n" "$TESTVAR2"
TESTVAR1="changed 1"
printf "Now I have changed TESTVAR1 to '%s'\n" "$TESTVAR1"
```


## Option 1: Execution

This requires the script to have the execution bit set in its permissions and a _shebang_ on the first line telling the shell which command to use to run the rest of the file.

```sh
chmod +x foo.sh
```

Speaking of permissions, you can use the "setuid" method to make the script run _as a different user_, if you want to. This is typically done with `sudo chmod +s foo.sh` and will allow regular users to run the application with elevated (root) privileges. [If you don't need to, don't do it](https://dev.to/moopet/sudo-like-you-want-to-187f)!

In `foo.sh` I've used `#!/bin/sh` which is conventionally a POSIX-compliant shell. Importantly, _it doesn't have to be the same as the shell I'm using_. You can have a shebang of `#!/usr/bin/bash` and run it from a `zsh` session if you like, but the script had better not have any zsh-specific syntax in it, because zsh won't be the program parsing it.

Even if the shebang says to run the script with the same shell we're using, it won't be the _same_ same shell. It'll be another instance of the application, called a _subshell_. It will have its own environment and only be able to read any of _our_ environment if we explicitly `export` it.

### Note about Process IDs

I'm going to use `echo $$` as a way of getting the PID (Process ID) for the current shell. That's one of many built-in shell variables we don't need to go into now, just know that it works.

### Let's go

```sh
$ pwd
/home/moopet

$ echo $$
8192

$ TESTVAR1="elephants"
$ export TESTVAR2="hens"

$ ./foo.sh

I am a script called ./foo.sh
My process ID is 11373
... and I have changed directory to /tmp/demo
TESTVAR1 started off as ''
and TESTVAR2 started off as 'hens'
Now I have changed TESTVAR1 to 'changed 1'

$ pwd
/home/moopet

$ echo $TESTVAR1
elephants
```

### So what happened?
Let's break this down.

* The script ran in a subshell (the process IDs of the parent and child were different)
* it **did not** automatically inherit its parent's environment (`TESTVAR1` was not populated)
* It **did** inherit `TESTVAR2` because that was explicitly _exported_[^1]
* Environment variables it set or modified appeared unchanged once control returned to the parent shell (`TESTVAR1` remained "elephants")
* The parent shell's working directory remained unchanged even though the subshell switched to a different one (/home/moopet vs /tmp/demo)


## Option 2: Sourcing

`source` is usually available as an alias for `.` (which is a dot). 

We'll _call it_ "sourcing" regardless of which we use. `source` is a clearer thing to write, but `.` is the one that's guaranteed to work by POSIX. That means that `.` will work on any shell that claims to be POSIX-compliant, whereas `source` might not.

Now, as we've seen, `foo.sh` has a shebang of `#!/bin/sh`, but that _doesn't matter_. The nice thing about these special first lines is that they start with a hash, and a hash mark denotes a comment in virtually all scripting languages, so it gets ignored. Clever, huh.

A side effect of this is that since it isn't being executed, it doesn't need the execute bit set. No `chmod`ding required.

Sourcing will run in the context of the current shell. It will have read- and write-access to the current environment and will execute as the current user.

It will also, perhaps unexpectedly given what I've just described, have access to the `$1`, `$2`, etc. positional parameters exactly as if it had been executed.

You can't `sudo source` something, and you can't use `setuid` on a script you source.

### Let's go, again.

```sh
$ pwd
/home/moopet

$ echo $$
8192

$ TESTVAR1="elephants"
$ export TESTVAR2="hens"

$ . ./foo.sh

I am a script called ./foo.sh...
My process ID is 8192...
... and I have changed directory to /tmp/demo
TESTVAR1 started off as 'elephants'
and TESTVAR2 started off as 'hens'
Now I have changed TESTVAR1 to 'changed 1'

$ pwd
/tmp/demo

$ echo $TESTVAR1
changed 1
```

### So what happened this time?
Let's break this down.

* The script ran in the same shell (the process IDs the script saw was the same as the interactive session)
* it had full read access to its environment (`TESTVAR1` and `TESTVAR2` were both populated)
* Environment variables it set or modified stayed set once interactive control returned (`TESTVAR1` remained "changed 1")
* The interactive shell's working directory stayed changed to that set in the script



## Conclusion: When to use what

If you need to affect the current shell, with something like an autocomplete extension, a prompt customisation or loading a set of aliases, go with _sourcing_.

If you want to guarantee what shell is executing the code, and to keep variables in their own separate space, go with _execution_.

--
Photo by [Magdalena Kula Manchee](https://unsplash.com/@magdalenakulamanchee) on Unsplash

[^1]: You don't have to use this syntax to export variables, you can export separately from the assignment, or you can define them on the same line as the script name, like `TESTVAR2="hens" ./foo.sh` if you want.