The path must be where the interpreter actually is on the machine, which may
differ from this table. which bash says where Bash is.
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 |
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
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
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.
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
$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
== compares strings, -eq compares numbers. Comparing two strings
with -eq compares 0 to 0, so the first branch always matches.
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
See Bash file test operators and other comparison operators.
Repeat something (for)
for item in one two three; do
echo "$item"
done
The above code would print:
one
two
three
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.
-e and -x are POSIX options and work in sh and Zsh too. See Bash option
flags for the rest.