Shell scripting basics
The moment you find yourself typing the same sequence of commands twice, you should put them in a script. A shell script is just a file of commands the shell runs top to bottom — but with variables, conditionals and loops, it becomes the way you automate the repetitive parts of operations: deployments, backups, setup, checks. Automation is the heart of DevOps, and shell scripting is where it starts. This lesson is writing a shell script that is reliable rather than a footgun.
Your first script
A script is a text file of commands. Create one:
#!/usr/bin/env bash
echo "Starting deploy"
pwd
echo "Done"
Two things make it a proper script:
- The shebang — the first line
#!/usr/bin/env bashtells the system to run the file with bash. It must be the very first line. - Execute permission —
chmod +x deploy.sh(the permissions lesson), so you can run it with./deploy.sh.
Run it: ./deploy.sh. The shell reads the file top to bottom and runs each line as if you had typed it. That
is the whole idea — a script is your commands, saved and repeatable.
Variables and arguments
Store values in variables and read them back with $:
name="asha"
echo "Hello $name" # Hello asha
greeting="Hello $name" # variables can contain other variables
Two rules that trip up everyone:
- No spaces around
=.name="asha"works;name = "asha"does not (bash reads it as a command). - Quote your variables. Use
"$name", not$name. If a variable contains spaces or is empty, an unquoted use breaks —rm $filewhen$fileis empty or has a space is a classic disaster. Always double-quote variable expansions; it prevents a whole category of bugs.
Scripts also receive arguments from the command line:
#!/usr/bin/env bash
echo "Deploying version $1 to $2" # $1 is the first argument, $2 the second
Run as ./deploy.sh v1.2 staging and $1 is v1.2, $2 is staging. $0 is the script's name, $# is
the number of arguments, and $@ is all of them. Arguments are how you make a script reusable instead of
hard-coding values.
Making decisions: if
Scripts make choices with if, testing a condition in [ ... ]:
#!/usr/bin/env bash
if [ "$1" = "production" ]; then
echo "Deploying to PRODUCTION — are you sure?"
elif [ -z "$1" ]; then
echo "No environment given" >&2 # >&2 sends this to standard error
exit 1 # exit with a non-zero code = failure
else
echo "Deploying to $1"
fi
Common tests: [ "$a" = "$b" ] (strings equal), [ "$a" != "$b" ], [ -z "$x" ] (empty), [ -n "$x" ]
(not empty), [ -f path ] (file exists), [ -d path ] (directory exists), and numeric [ "$n" -gt 5 ]
(greater than). Note the spaces inside the brackets are required. exit 1 stops the script with a failure
code — which matters because whatever runs your script (a CI pipeline) checks that code.
Repeating: loops
Do something for each item with a for loop:
for env in staging production; do
echo "Checking $env"
done
for file in /var/log/*.log; do # loop over matching files
echo "Rotating $file"
done
And a while loop repeats until a condition stops being true. Loops are how one script handles many things —
every server, every file, every environment — without repeating yourself.
The two lines that make scripts safe
Here is the most valuable thing in this lesson. By default, a shell script keeps going after a command
fails, and it treats an unset variable as an empty string — both of which cause quiet, dangerous bugs (a
deploy that "succeeds" after a step failed; an rm -rf "$DIR/" where $DIR was never set, deleting /).
Guard against this by starting serious scripts with:
#!/usr/bin/env bash
set -euo pipefail
set -e— exit immediately if any command fails, instead of ploughing on. A failed step stops the script.set -u— treat an unset variable as an error, instead of silently empty. Catches typos and missing arguments.set -o pipefail— a pipeline fails if any command in it fails, not just the last one.
These three (set -euo pipefail) turn a script from "runs to the end no matter what went wrong" into "stops
the moment something is wrong" — which is exactly what you want for a deploy or a backup. Add them to the top
of every non-trivial script; it is the single biggest reliability upgrade you can make, and its absence is
behind a lot of "the script said it worked but it didn't".
Check your work
A script = a file of commands run top to bottom, with a shebang (#!/usr/bin/env bash, first line)
and execute permission (chmod +x), run as ./script.sh.
Variables: name="asha" (no spaces around =), read with $name; always double-quote ("$name")
to survive spaces/empties (unquoted is a classic bug). Arguments: $1,$2,… ($0 name, $# count,
$@ all) make scripts reusable.
if tests [ ... ] (spaces required): =/!=, -z/-n (empty/not), -f/-d (file/dir exists),
-gt etc.; exit 1 = failure (checked by CI). Loops: for x in ...; do ... done, while.
Make scripts safe: start with set -euo pipefail — -e exit on any failure, -u error on unset
variable, -o pipefail fail if any piped command fails. The biggest reliability upgrade; without it scripts
"succeed" after a step failed.
Practice
- Write and run a script with a shebang that prints a greeting;
chmod +xit first. - Add an argument so it greets
$1, and double-quote the variable; test with an argument containing a space. - Add an
ifthat exits with code 1 (and a message to stderr) when no argument is given. - Write a
forloop over a list of environment names, printing an action for each. - Add
set -euo pipefailto a script, then deliberately reference an unset variable and a failing command; show how the script now stops. - Explain, with an example, why unquoted variables and missing
set -eare dangerous in a deploy script.
Official documentation
- GNU Bash manual — The reference for shell scripting syntax.
- Google Shell Style Guide — Practical conventions for readable, safe scripts.
- ShellCheck — A linter that catches the common scripting mistakes (quoting,
set -e).
Next: SSH, keys and connecting to a server.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship