105.2 Customize or Write Simple Scripts

Weight: 4

Goal: Customize existing scripts, or write simple new Bash scripts.


1. What is a Shell Script?

A shell script is a plain text file containing a sequence of shell commands. Instead of typing the commands one by one, you save them in a file and run that file. The shell reads the file from top to bottom and runs each line.

A minimal script:

#!/bin/bash
echo "Hello, world"

To run it:

$ chmod +x hello.sh         # make it executable
$ ./hello.sh                # run it
Hello, world

2. The Shebang Line (#!)

The very first line of a script can begin with #! followed by the path to an interpreter. This is called the shebang (or hashbang). It tells the kernel which program should run the script.

#!/bin/bash         # use Bash
#!/bin/sh           # use the system's POSIX shell
#!/usr/bin/python3  # use Python 3

Key points for the exam:


3. Script Location, Ownership, Permissions, and SUID

To run a script, the kernel needs:

  1. Read permission — so it can read the file.
  2. Execute permission — so it knows the file may be run.
$ chmod +x myscript.sh        # add execute permission for all
$ chmod 755 myscript.sh       # rwxr-xr-x — owner can edit/run, others can run
$ chmod 700 myscript.sh       # rwx------ — only the owner can do anything

Where to put scripts

If a directory is in PATH, you can run the script by name (myscript.sh). Otherwise, you must give the full or relative path (./myscript.sh).

SUID on scripts

The SUID bit (chmod u+s) normally makes a program run with the privileges of its owner. Linux deliberately ignores SUID on shell scripts for security reasons — setting it has no effect. The exam expects you to know this: do not rely on SUID for scripts; use sudo instead.


4. Variables and Quoting in Scripts

Defining and using variables

NAME="Alice"
echo "Hello, $NAME"           # → Hello, Alice
echo "Hello, ${NAME}!"        # braces let you put text directly after

No spaces around the = sign — NAME = "Alice" is a syntax error.

Special variables

Variable Meaning
$0 The script’s own name.
$1, $2, …, $9 The first, second, … positional arguments.
$# Number of arguments passed.
$@ All arguments as separate words.
$* All arguments as a single word.
$? Exit status of the last command (see section 6).
$$ PID (process ID) of the current shell.
$! PID of the last background job.

Quoting

$ NAME=Alice
$ echo "Hello, $NAME"      # Hello, Alice
$ echo 'Hello, $NAME'      # Hello, $NAME
$ echo "Cost: \$5"         # Cost: $5

5. Command Substitution

Command substitution runs a command and inserts its output into another command.

TODAY=$(date)               # preferred form
TODAY=`date`                # older form, same meaning
echo "Today is $TODAY"

You can use it anywhere a string is expected:

echo "There are $(ls | wc -l) files here"

The exam may show either form ($(…) or backticks `…`). Both work.


6. Exit Status and Testing Return Values

Every command returns an exit status (also called a return code) when it finishes:

The exit status of the last command is in $?:

$ ls /etc
... output ...
$ echo $?
0
$ ls /nope
ls: cannot access '/nope': No such file or directory
$ echo $?
2

A script can return its own exit status using exit:

exit 0      # success
exit 1      # general error

If exit is omitted, the script returns the status of its last command.


7. Chained Commands: &&, ||, ;

These connect commands and act on the exit status of the previous one.

Operator Meaning
cmd1 ; cmd2 Run cmd1, then run cmd2 regardless.
cmd1 && cmd2 Run cmd2 only if cmd1 succeeded (exit 0).
cmd1 || cmd2 Run cmd2 only if cmd1 failed (non-zero exit).

Examples:

mkdir /tmp/work && cd /tmp/work     # cd only if mkdir worked
ping -c1 host || echo "host is down"
make && make install || echo "build failed"

8. The test Command and [ … ]

test evaluates a condition and returns exit status 0 (true) or 1 (false). It is the heart of if statements.

These two lines are identical:

test -f /etc/passwd
[ -f /etc/passwd ]

Note the spaces inside the brackets[-f file] is wrong.

File tests

Test True if …
-e FILE FILE exists.
-f FILE FILE exists and is a regular file.
-d FILE FILE exists and is a directory.
-r FILE FILE is readable.
-w FILE FILE is writable.
-x FILE FILE is executable.
-s FILE FILE exists and is not empty.
-L FILE FILE is a symbolic link.

String tests

Test True if …
-z STRING STRING is empty (zero length).
-n STRING STRING is not empty.
STR1 = STR2 strings are equal.
STR1 != STR2 strings differ.

Numeric tests

Test Meaning
N1 -eq N2 equal
N1 -ne N2 not equal
N1 -lt N2 less than
N1 -le N2 less than or equal
N1 -gt N2 greater than
N1 -ge N2 greater than or equal

Pitfall: use -eq for numbers and = for strings. Mixing them up is a common exam trap.

Combining tests

[ -f "$f" ] && [ -r "$f" ]

9. if Statement

if [ -f /etc/passwd ]; then
    echo "passwd exists"
elif [ -f /etc/master.passwd ]; then
    echo "BSD-style passwd file"
else
    echo "no passwd file found"
fi

Key points:


10. Loops: for and while

for — iterate over a list

for name in alice bob carol; do
    echo "Hello, $name"
done

Loop over files:

for f in *.txt; do
    echo "Found: $f"
done

Loop over numbers using seq:

for i in $(seq 1 5); do
    echo "Number $i"
done

seq generates a sequence of numbers:

$ seq 3            # 1 2 3
$ seq 2 5          # 2 3 4 5
$ seq 0 2 10       # 0 2 4 6 8 10 (start, step, end)

C-style for (Bash only):

for ((i=1; i<=5; i++)); do
    echo $i
done

while — loop while a condition is true

count=1
while [ $count -le 5 ]; do
    echo "Count is $count"
    count=$((count + 1))
done

until is the opposite — loops while the condition is false:

until [ $count -gt 5 ]; do
    ...
done

11. read — Get Input from the User

echo -n "Enter your name: "
read name
echo "Hello, $name"

Shortcuts:

read -p "Enter your name: " name      # built-in prompt
read -s password                       # silent input (no echo)
read first second third                # split input into multiple vars

read is also useful for reading a file line by line:

while read line; do
    echo "Got: $line"
done < /etc/passwd

12. exec — Replace the Current Shell

exec replaces the current shell process with the given command. The shell does not return after exec finishes — the running script ends.

exec /usr/bin/python3 myscript.py     # the bash script is replaced by python

A common use is redirecting all output of a script to a file:

exec > /var/log/myscript.log 2>&1
echo "this goes into the log file"

After that exec line, all subsequent output of the script is sent to the log.


13. Conditional Mailing to the Superuser

A common scripting pattern: when something goes wrong, email root.

The mail command (also called mailx on some systems) sends mail from the command line:

echo "Disk is almost full" | mail -s "ALERT" root

In a script:

if [ $(df / | awk 'NR==2 {print $5}' | tr -d '%') -gt 90 ]; then
    echo "Root filesystem is over 90% full" | mail -s "Disk Alert" root
fi

14. Putting It Together — A Complete Example

#!/bin/bash
# backup.sh — back up /home if the destination directory exists

DEST="/backup"

if [ ! -d "$DEST" ]; then
    echo "Destination $DEST does not exist" | mail -s "Backup failed" root
    exit 1
fi

for user in $(ls /home); do
    tar -czf "$DEST/${user}-$(date +%F).tar.gz" "/home/$user" \
        && echo "Backed up $user" \
        || echo "Failed: $user" | mail -s "Backup error" root
done

exit 0

This single script touches almost every concept in this objective: shebang, variables, if, for, command substitution, &&/||, conditional mail, and exit.


15. Quick Reference: Files and Commands for the Exam

Keywords and commands:

Other things you must know:


16. Likely Exam Questions (Self-Check)

  1. What does the first line #!/bin/bash do? It tells the kernel to use /bin/bash to interpret the script.

  2. A script needs to run only if the previous command succeeded. Which operator do you use? &&. For example: cmd1 && cmd2.

  3. What is the exit status of a successful command? 0.

  4. How do you check if a file exists and is readable? [ -r filename ] (or test -r filename).

  5. What is the difference between = and -eq in a test? = compares strings; -eq compares integers.

  6. What does $? contain? The exit status of the most recently executed command.

  7. You set SUID on a Bash script. Will it run with the owner’s privileges? No. Linux ignores the SUID bit on shell scripts. Use sudo instead.

  8. What does $(date) do? Runs the date command and substitutes its output in place.

  9. How do you read a line of input from the user into a variable named answer? read answer.

  10. Write a for loop that prints numbers 1 through 5 using seq. bash for i in $(seq 1 5); do echo $i; done

  11. What is the purpose of exec > logfile 2>&1 at the top of a script? Redirects all standard output and standard error of the script to logfile.

  12. Where would you place a script that should be available to all users on the system? /usr/local/bin/ (or /usr/local/sbin/ for admin scripts).