Basic variable definition

Traditionally, shell variables are declared, defined and initialized at the same time. This is done similarly to other programming languages, with the variable name on the left and the value on the right.

FOO=bar

Note that no spaces are allowed between the variable, the = and the value. The following do not parse as you would expect.

FOO= bar  # executes bar with FOO set to the empty string
FOO =bar  # executes FOO with =bar as the first argument
FOO = bar # executes FOO with = and bar as the first and second arguments

If the value you want to set the variable to contains spaces, you must enclose it in quotes like this.

FOO="bar baz"

Basic variable interpolation

The most common use for setting a variable is interpolation into another command. The following command prints the contents of the variable FOO to your terminal, which would be bar baz if it was set like the previous section.

$ echo $FOO
bar baz

It is also possible to interpolate in a variable assignment.

$ FOO="$FOO qux "
$ echo $FOO
bar baz qux 

If you want to interpolate a variable with a string which has alphanumeric characters after the interpolation, you need to use an alternative syntax.

$ echo $FOO7

$ echo ${FOO}7
bar baz qux 7

Special variables

There are a large number of special variables, which will have been pre-defined by your shell. Some of which may be modified to alter your shell's behaviour.

I'm not going to list them all, see your shell's documentation for that, but I'm going to describe the 3 most commonly used from the command line.

$?

$? is the return code of the last program that was executed. This is useful if a command produces no output, and you don't know if that is a good thing or a bad thing.

It will be 0 if a command succeeded.

$ true # true always succeeds
$ echo $?
0

It will not be 0 if the command failed.

$ false # false always fails
$ echo $?
1    

PS1

PS1 is the specification of how your shell will display its prompt.

$ PS1="% "
% echo My prompt is $PS1
My prompt is % 

This does not need to be a fixed string, your shell will provide a formatting language, and support embedding of commands. As usual, see the documentation for more details.

PATH

PATH is not specific to your shell, but the most common way of interacting with it is through a shell.

It is a colon separated list of paths to directories that contain executables.

% echo $PATH
/usr/local/bin:/usr/bin:/bin

When you run a command without specifying a full path to the executable it will use PATH to find it, using directories specified from left to right.

You can alter it like other variables to allow other executables to be found.

% cat ~/bin/my-command
#!/bin/sh
echo "Hello World"
% PATH="$HOME/bin:$PATH"
% my-command
Hello World

Scopes

There are 3 classes of scopes in most shells.

  • Global variables

    FOO is a global variable, this is the default

  • Exported (environment) variables (if you alter PATH or export FOO)

    These will be inherited by processes you execute. Any variables that were exported to you will be inherited, so you may alter the PATH for a subshell.

    You can also export FOO to a subshell with the export command.

    % sh
    $ echo FOO is $FOO
    FOO is 
    $ exit
    % export FOO
    % sh
    $ echo FOO is $FOO
    FOO is bar baz qux 
    

    You can only unexport a variable by unsetting it, then re-setting it.

    % unset FOO
    % FOO="bar baz qux "
    % echo $FOO
    bar baz qux 
    % sh
    $ echo FOO is $FOO
    FOO is 
    
  • Local variables (not basic shell)

    Local variables are only valid inside functions. These allow a variable to be re-used across functions, and is one requirement to being able to write recursive functions.

    % func () { local FOO=zxc; }
    % func
    % echo $FOO
    bar baz qux 
    

Operations on variables

As shown previously, it is possible to append to a variable by interpolating, however this requires typing out the variable twice. Shell also supports an append operator in the form of +=:

% FOO+=asdf
% echo $FOO
foo bar bas asdf

There are also more advanced interpolation operations, these only work with the braced form of interpolation (${FOO}). For a list of all of them, see the Parameter Expansion section of the documentation.

Default value

If you want to interpolate a variable, which may not have been set, but you have a sensible default.

% unset FOO
% echo $FOO

% echo ${FOO-bar}
bar

This is terser, but less readable than doing the following.

if [ -z "$FOO" ]; then
  echo bar
else
  echo "$FOO"
fi

Suffix and prefix stripping

It can be convenient to remove a portion of a string, such as removing the file extension of a file,

% FOO=bar
% echo ${FOO%r}
ba

These is also a prefix strip using the # character instead of the %.

% echo ${FOO#b}
ar

It may help to think that the % looks like an s, hence suffix.

Decompress directory example

As an example of combining some things that have been learned, here is a shell function that decompresses all gzipped files in a directory.

If a directory is not specified, it uses the current directory.

decompress() {
  for gzipped in "${1-.}"/*.gz # Use current dir if not specified
  do
    gunzip -c "$gzipped" >"${gzipped%.gz}" # Strip .gz from path
  done
}