Shell Scripting Cheatsheet

Anything you can type in your terminal, you can put in a file and have the machine do for you, the same way every time. That file is a script, and this is how you write one with Bash. Most of it also works in other POSIX shells like sh and Zsh.

The companion to this page is the Command Line Cheatsheet, which covers the commands you put in a script.

What is a script?

A file that can be executed is one of two things: a binary file, containing machine code compiled from source, or a script, a text file that an interpreter reads and runs.

The first line of a script, the shebang, names that interpreter:

#!/bin/bash

This one says the file should be run by Bash. There must be no space between #! and the path.

A script whose interpreter is a shell is a shell script. A script whose interpreter is PHP is a PHP script: still a script, but not a shell script. What you may write inside depends entirely on the interpreter β€” in a Bash script, anything you could type at a Bash prompt.

Shebang Script contents
#!/bin/sh Bourne shell commands
#!/bin/bash Bash shell commands
#!/bin/zsh Z shell commands
#!/usr/bin/node Node.js code
#!/usr/bin/php PHP code
#!/usr/bin/python Python code
#!/usr/bin/ruby Ruby code
Note

The path must be where the interpreter actually is on the machine, which may differ from this table. which bash says where Bash is.

Writing and running a script

Create a script (printf, nano)

Write the whole thing in one command:

$> printf '#!/bin/bash\necho Hello World\n' > test.sh
πŸ“šMore information

The printf (print format) command is similar to the echo command but it has better support for special characters like new lines (\n).

Or open an editor and type it in:

$> nano test.sh

Run a script (chmod +x, ./script)

A file you have just created is not executable. Make it so, then run it by its path:

$> chmod +x test.sh
$> ./test.sh
Hello World
πŸ’ŽTip

The ./ matters: without it, the shell looks for test.sh in the directories of your PATH instead of in the current directory. See make a file executable and run a program.

Know where the script runs (cd)

A script starts in the directory it was run from, not the one it lives in. Use cd to move around:

#!/bin/bash
pwd
cd /home
pwd

Run from /some/where/over/the/rainbow, this prints:

/some/where/over/the/rainbow
/home

Variables

Use a variable

No spaces around the =:

#!/bin/bash
FOO=bar
echo "$FOO" # prints "bar"

Quote the variable when you declare it and when you use it, so that a value containing whitespace (spaces, new lines, etc) stays one value:

#!/bin/bash
FOO="bar baz"
echo "$FOO" # prints "bar baz"

Store the output of a command ($(...))

#!/bin/bash
FILES=$(ls -1)
NUMBER_OF_FILES=$(echo "$FILES" | wc -l)
echo "There are $NUMBER_OF_FILES files"

This script would print There are 10 files if there are 10 files in the current directory.

πŸ“šMore information

You will also see the older backtick form, FILES=`ls -1`. It does the same thing, but unlike backticks, $(...) can be nested.

Read and set environment variables (export)

Environment variables are ordinary variables in a script:

#!/bin/bash
echo "$PATH"

Set one the way you would in any shell:

#!/bin/bash
export FOO=bar
Note

$FOO will only be set for this script and the programs it starts. The shell you ran the script from is unchanged.

Use the arguments the script was given ($1, $@)

Bash has a number of special variables which are always available:

Variable Description
$0 Name of the command being executed.
$1 First argument passed on the command line (then $2, $3…).
$@ All arguments passed.
$? Exit value of the last executed command.

For example, this script says hello to the name passed as the first argument:

#!/bin/bash
echo "Hello $1"
$> ./hello.sh World
Hello World

Logic

Test a condition (if)

Bash has a classic if/then/else construct:

#!/bin/bash
FOO="bar"
if [[ "$FOO" == "foo" ]]; then
echo "FOO is foo"
elif [[ "$FOO" == "bar" ]]; then
echo "FOO is bar"
else
echo "FOO is something else"
fi
Warning

== compares strings, -eq compares numbers. Comparing two strings with -eq compares 0 to 0, so the first branch always matches.

πŸ“šMore information

The [[ ]] syntax is a Bash test construct. Also see Bash other comparison operators.

Test a file or an empty variable (test)

The test built-in command is another way to write some conditions:

#!/bin/bash
EMPTY_VAR=
FULL_VAR="full"
FILE="/path/to/some/file"
if test -z "$EMPTY_VAR"; then
echo "variable is empty"
fi
if test -n "$FULL_VAR"; then
echo "variable is not empty"
fi
if test -f "$FILE"; then
echo "file exists"
else
echo "file does not exist"
fi
πŸ“šMore information

Repeat something (for)

for item in one two three; do
echo "$item"
done

The above code would print:

one
two
three
πŸ“šMore information

Bash also has while and until. See loops & branches.

Reuse a piece of code (functions)

Isolate a piece of code in a function. Inside it, $1, $2 and so on are the function’s arguments, not the script’s:

#!/bin/bash
print_hello() {
echo "Hello $1"
}
print_hello World

This script would print Hello World.

Keep a variable inside a function (local)

Normal Bash variables have no scope: one set anywhere is visible in the whole file and in every function. Use the local keyword to limit one to the function it is declared in:

#!/bin/bash
print_hello() {
local name="$1"
echo "Hello $name"
}
print_hello World
echo "$name"

This script would print Hello World and an empty line, since $name is only defined within the print_hello function.

When it goes wrong

Stop on the first error (set -e)

A script keeps going after a command fails, which is not what you usually want. set -e aborts it instead:

#!/bin/bash
set -e
echo Hello World
cat file-that-does-not-exist
echo Done
Hello World
cat: file-that-does-not-exist: No such file or directory

Done is never printed.

See what the script is doing (set -x)

set -x prints each command, with a leading +, before running it:

#!/bin/bash
set -x
echo Hello World
+ echo Hello World
Hello World

Combine the two with set -ex.

πŸ“šMore information

-e and -x are POSIX options and work in sh and Zsh too. See Bash option flags for the rest.

References