Course outline · 0% complete

0/29 lessons0%

Course overview →

Variables and PATH

lesson 7-1 · ~10 min · 19/29

Recall the kill example from lesson 6-2, which wrote pid=$! and then used $pid a line later. That was a shell variable, storing a value so a later command could reuse it.

Variables are the shell's memory, and this lesson makes them official.

Shell variables

Variables are how real software gets configured. API keys, database addresses, and which mode to run in are all handed to programs through the environment rather than written into the code. And PATH, at the end of this lesson, explains the single most common broken-machine symptom: command not found for a tool that was just installed.

A variable stores a value under a name:

city="Lisbon"
echo "Next stop: $city"

Three rules bite beginners here:

  1. No spaces around =. Writing city = "Lisbon" fails, because the shell reads city as a command name.
  2. Read a variable with $name. Leave off the dollar sign and you get the literal word instead of the value.
  3. Use double quotes around text containing $name. Inside double quotes a variable expands, and inside single quotes it stays literal.

Variables you never set also exist, because the OS provides a set of environment variables such as $HOME (your home directory) and $USER (your username). The env command lists all of them.

One name, many uses

A single variable read twice. This is the practical argument for variables: the city name is written once as data, so changing Lisbon to any other city updates every place it appears.

city="Lisbon"
echo "Next stop: $city"
echo "I love $city so much"

Output

Next stop: Lisbon
I love Lisbon so much

Both echo lines expanded $city to the stored value because they used double quotes. The assignment itself must stay glued together as city="Lisbon", with no spaces flanking the =, or the shell would try to run city as a command.

export: sharing with child processes

A plain variable belongs to your shell only. Programs your shell starts (child processes, lesson 6-2) don't see it unless you export it:

snack="olives"     # private to this shell
export snack        # now children inherit it

That's the actual difference between a "shell variable" and an "environment variable": exported ones travel to children. The demo below proves it with bash -c '...', which starts a brand-new child shell to run one command.

Proving what export changes

Before the export, the child shell finds nothing and prints empty brackets. After it, the same command sees the value.

snack="olives"
bash -c 'echo "child sees: [$snack]"'
export snack
bash -c 'echo "child sees: [$snack]"'

Output

child sees: []
child sees: [olives]

The two identical commands producing different output is the entire lesson: only the export happened in between. The single quotes around each child command are also load-bearing, since they keep $snack unexpanded until the child reads it, which is the single-versus-double quote rule from earlier put to work deliberately.

$ cowsay→ the shell checks each PATH directory in order:/usr/local/bin/usr/bin/binfound: /bin/cowsay ✓
PATH is a colon-separated list of directories. The shell tries each in order and runs the first match it finds.

PATH: how the shell finds commands

Typing ls works even though nothing in that command says where the ls program lives. The shell resolves it by reading the environment variable PATH, a colon-separated list of directories:

/usr/local/bin:/usr/bin:/bin

The shell checks each of those directories in order and runs the first ls it finds, and which ls reports which one won.

This also explains a rite of passage. A script you just wrote will not run by name, because the current directory is not on PATH. That is why it has to be invoked as ./myscript.sh, the explicit relative path from lesson 2-1, and unit 8 does exactly that.

A script called deploy.sh in your current directory still gives command not found when invoked as deploy.sh, because the current directory is not in PATH, so the shell never looks there.

The shell searches only the directories listed in PATH, and . is deliberately left out of that list for security reasons. If the current directory were searched, merely visiting a directory containing a hostile file named ls could get it run by accident. There are two ways around it: invoke the script as ./deploy.sh to give an explicit path, or move it into a directory that is on PATH, such as /usr/local/bin.

Two variables can be combined inside a single quoted string, and the shell expands each one in place. Here name holds Ada and language holds bash, and one echo weaves both into a sentence.

name="Ada"
language="bash"
echo "$name is learning $language"

Output

Ada is learning bash

Both assignments keep the no-spaces rule around their = signs. The echo uses double quotes, which is what allows $name and $language to expand while the surrounding words stay as literal text.