Filters
How to search
  • Plain words find names, texts and examples.
  • "two words" finds the exact phrase.
  • --name or -x finds an option.
  • #word looks only inside commands.
  • @grep finds lessons and labs that use grep.
  • Filters: cat:network · in:labs · in:notes · level:rhcsa · ch:8 · root:yes. Put ! before a word to leave it out. OR between words means one of them.
  • ↑ ↓ to move · Enter to open · Esc to close · / to search

Chapter 19Name in the book: An Introduction to Automation with Bash Shell Scripting

Automate tasks with Bash scripts

A shell script is a text file with commands. With variables, input, conditions and loops, a script turns a manual task into one command. This chapter covers the core elements, positional parameters, variables, if/then/else, && and ||, for, while, until, case, and how to debug a script.

  • 12 Lessons
  • 11 Commands
  • 1 Labs

$ run as a normal user# run as root (or with sudo)

Exam objectives in this chapter

  • Run code only when a test is true (if, test, [ ])
  • Use loops (for and others) on files and command-line input
  • Use script inputs ($1, $2 and so on)
  • Use the output of commands inside a script

19.1

Write your first script

A script starts with the shebang line (#!/bin/bash), which says which program runs it. Make it executable and run it with its path.

  1. Create the file.
    /usr/local/bin/hello.sh
    #!/bin/bash
    # hello.sh - first script
    echo "Hello, $USER. Today is $(date +%A)."
    exit 0
  2. Make it executable and run it.
    chmod +x /usr/local/bin/hello.sh
    hello.sh
    ./hello.sh
    bash hello.sh
PartWhy
#!/bin/bashUse bash (not another shell)
# commentExplain what the script does
exit 0Tell the caller: success (other numbers = an error)

Remember

Folders in $PATH (like /usr/local/bin or ~/bin) let you start the script by name. Elsewhere use ./name.sh.

19.2

Read input and use variables

A variable stores a value: NAME=value (no spaces around =), and $NAME uses it. read gets input from the user. $(command) stores the output of a command.

input.sh
#!/bin/bash
read -p "Which folder? " DIR
COUNT=$(ls -1 "$DIR" | wc -l)
echo "$DIR has $COUNT entries"
./input.sh
Which folder? /etc
/etc has 182 entries
  • Always put variables in double quotes: "$DIR". Without quotes, names with spaces break.
  • export NAME makes a variable visible for programs the script starts.
  • ${NAME:-default} uses default when NAME is empty.

19.3

Use positional parameters

Arguments after the script name arrive as $1, $2 … The script can check how many it got.

VariableContains
$0The script name
$1 … $9, ${10}The arguments
$#The number of arguments
"$@"All arguments, each one separate
$?Exit status of the last command
$$PID of the script
adduser.sh
#!/bin/bash
# usage: adduser.sh NAME [GROUP]
if [ $# -lt 1 ]; then
    echo "usage: $0 NAME [GROUP]" >&2
    exit 1
fi
useradd -G "${2:-users}" "$1" && echo "user $1 created"

19.4

Use tests and if then else

if runs commands only when a test succeeds (exit status 0). test or [ … ] checks files, strings and numbers.

checkfile.sh
#!/bin/bash
FILE=$1
if [ -f "$FILE" ]; then
    echo "$FILE is a file"
elif [ -d "$FILE" ]; then
    echo "$FILE is a folder"
else
    echo "$FILE does not exist"
fi
TestTrue when
-f FILE, -d DIR, -e PATHFile / folder / anything exists
-r, -w, -x FILEReadable / writable / executable
-z "$S", -n "$S"String empty / not empty
"$A" = "$B", "$A" != "$B"Strings equal / not equal
$X -eq $Y, -ne, -lt, -le, -gt, -geNumbers: equal, not equal, less, less/equal, greater, greater/equal
!Not

Watch out

Spaces are needed inside the brackets: [ -f "$FILE" ], not [-f "$FILE"].

Commands in this lesson

19.5

Use && and ||

A && B runs B only when A worked. A || B runs B only when A failed. Good for short checks.

mkdir /tmp/work && cd /tmp/work
ping -c1 -W1 server2 >/dev/null || echo "server2 is down"
[ -d /backup ] || mkdir /backup
grep -q lisa /etc/passwd && echo "lisa exists" || echo "lisa missing"

19.6

Repeat with for

for runs the same commands for each item of a list: words, files, numbers or command output.

loops.sh
#!/bin/bash
for USER in anna bob carol; do
    useradd "$USER"
done
 
for FILE in /var/log/*.log; do
    echo "$FILE: $(wc -l < "$FILE") lines"
done
 
for i in {1..5}; do
    echo "round $i"
done
 
for HOST in $(cat hosts.txt); do
    ping -c1 -W1 "$HOST" >/dev/null && echo "$HOST up"
done

19.7

Repeat with while and until

while repeats while a test is true; until repeats until it becomes true. while read reads a file line by line.

wait.sh
#!/bin/bash
# wait until the web server answers
until curl -fs http://localhost >/dev/null; do
    echo "waiting for httpd..."
    sleep 2
done
echo "httpd is up"
 
# read a list line by line
while read -r NAME GROUP; do
    useradd -G "$GROUP" "$NAME"
done < users.txt

19.8

Choose with case

case compares one value with patterns — cleaner than many elif lines. Common for start/stop/status scripts.

service.sh
#!/bin/bash
case "$1" in
    start)
        systemctl start httpd ;;
    stop)
        systemctl stop httpd ;;
    status|state)
        systemctl status httpd ;;
    *)
        echo "usage: $0 {start|stop|status}" >&2
        exit 1 ;;
esac

19.9

Debug a script

bash -x prints every command before it runs, with the values filled in. bash -n checks the syntax without running anything.

bash -n backup.sh
bash -x backup.sh
+ DIR=/etc
+ tar -czf /backup/etc.tgz /etc
SettingEffect
set -x / set +xTrace on / off from this line
set -eStop at the first command that fails
set -uError on unset variables
set -o pipefailA pipe fails if any part fails

At work

Start real scripts with set -euo pipefail and check them with shellcheck (from EPEL): it finds quoting bugs and other common mistakes.

Exam tip

The exam can ask for a small script: for example "write a script that creates users from a list" or "prints a message based on an argument". Test it with good and bad input, and make it executable.

19.10

Remember the main points

The most important facts of this chapter, in short.

  • A script starts with #!/bin/bash and needs execute permission (chmod +x).
  • Variables: NAME=value (no spaces), use them as "$NAME"; $(command) puts the output of a command in a variable.
  • Arguments: $1, $2 …, all with "$@", the number with $#; $? is the exit code of the last command.
  • if [ test ]; then … elif …; else …; fi checks conditions; test / [ ] compares numbers (-eq, -gt), text (=, -z) and files (-f, -d, -e).
  • && runs the next command only on success, || only on failure.
  • Loops: for x in LIST; do …; done, while CONDITION; do …; done, until.
  • case "$1" in start) … ;; stop) … ;; *) … ;; esac picks one of many choices.
  • Debug with bash -x script.sh; end with exit 0 (ok) or another number (error).

19.11

Learn the key terms

The words of this chapter in simple English.

TermMeaning
ShebangThe first line #!/bin/bash: which program runs the script.
VariableA name that holds a value.
Positional parameterAn argument of the script: $1, $2 …
Exit codeThe number a command returns: 0 = ok, other = error.
Command substitution$(cmd): the output of a command used as a value.
ConditionA test that is true or false.
LoopCode that runs many times.
Here documentText inside the script given to a command with <<EOF.

19.12

Check what you learned

Think about the answer first, then open it.

1What must the first line of a Bash script be?

#!/bin/bash.

2How do you check in a script that the user gave at least one argument?

if [ $# -lt 1 ]; then echo "usage: $0 NAME"; exit 1; fi.

3How do you put today's date in a variable?

TODAY=$(date +%F).

4How do you test if /etc/hosts is a normal file?
5Write a loop that creates the users anna, bob and carl.

for u in anna bob carl; do useradd "$u"; done.

6How do you see the exit code of the last command?
7How do you see every line a script runs, for debugging?

bash -x script.sh (or set -x inside the script).

8Why do you write "$1" with quotes?

So a value with spaces stays one argument.

Practice

All commands in this chapter