Scripts: Just a List of Commands (For Starters)
In its most basic form, a Bash script is simply a series of commands stored in a file. This might seem trivial at first, but it saves you the effort of retyping the same commands repeatedly.
Example:
Bash
#!/bin/bash
# Cleanup log files (requires root privileges)
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."
This script, when executed, will navigate to the /var/log
directory and clear the contents of the messages
and wtmp
log files.
Why Bother with Scripts?
You might be thinking, “Why not just type these commands directly?” Well, scripts offer several advantages:
- Efficiency: No more retyping long sequences of commands.
- Re-usability: Easily reuse and share your scripts.
- Maintainability: Modify and update your scripts as needed.
- Automation: Schedule scripts to run automatically (e.g., with
cron
). - Flexibility: Customize scripts for specific tasks.
Adding a Little Intelligence
Let’s enhance our cleanup script a bit:
Bash
#!/bin/bash
# Cleanup, version 2
# Run as root, of course.
# TODO: Add code here to print error message and exit if not root.
LOG_DIR=/var/log
# Variables are better than hard-coded values.
cd "$LOG_DIR" # Quoting variables prevents issues with spaces.
cat /dev/null > messages
cat /dev/null > wtmp
echo "Logs cleaned up."
exit 0 # Explicitly exit with success code.
We’ve added a few things:
- Shebang: The
#!/bin/bash
line tells the system to use the Bash interpreter. - Comments: Lines starting with
#
are comments to help explain the code. - Variables: We’ve used a variable
LOG_DIR
to store the directory path. - Exit Command: The
exit 0
command ensures a clean exit from the script, with a success code. - Quoting variables: Added quotes around the $LOG_DIR variable to prevent issues if there are spaces in the path.
- TODO Comment: added a comment to remind the reader to add root checking code.
Making it More Robust
Let’s take it a step further:
Bash
#!/bin/bash
# Cleanup, version 3
LOG_DIR=/var/log
ROOT_UID=0 # Only users with $UID 0 have root privileges.
LINES=50 # Default number of lines saved.
E_XCD=86 # Can't change directory?
E_NOTROOT=87 # Non-root exit error.
# Run as root, of course.
if [ "$UID" -ne "$ROOT_UID" ]; then
echo "Must be root to run this script."
exit "$E_NOTROOT"
fi
if [ -n "$1" ]; then
LINES="$1"
fi
cd "$LOG_DIR" || {
echo "Can't change to $LOG_DIR." >&2
exit "$E_XCD"
}
tail -n "$LINES" messages > mesg.temp # Save last section of message log file.
mv mesg.temp messages # Rename it as system log file.
cat /dev/null > wtmp
echo "Log files cleaned up."
exit 0
In this version:
- We’ve added error checking to ensure the script runs as the root user.
- We’ve introduced the concept of command-line arguments (
$1
). - We’re now saving the last 50 lines of the
messages
log file instead of wiping it completely. - We replaced the pwd check with the more efficient
cd || {}
construct. - We’ve added quotes around all variables within the conditional statements.
The Shebang (#!)
The #!
at the beginning of a script is crucial. It tells the system which interpreter to use to execute the script. Common shebangs include:
#!/bin/bash
(Bash shell)#!/bin/sh
(Default Bourne shell, often a link to Bash)#!/usr/bin/perl
(Perl interpreter)#!/usr/bin/python3
(Python interpreter)
Make sure the path to the interpreter is correct!
Building a Scripting Toolkit
As you write more scripts, you’ll start to accumulate reusable code snippets. These can be functions, error-handling routines, or other useful bits of logic. Organize these into a personal “scripting toolkit” to save time and effort in future projects.
For example, here’s a script prolog to check for the correct number of command-line arguments:
Bash
#!/bin/bash
E_WRONG_ARGS=85
script_parameters="-a -h -m -z"
# -a = all, -h = help, etc.
if [ "$#" -ne "$Number_of_expected_args" ]; then
echo "Usage: $(basename "$0") $script_parameters"
# $(basename "$0") is the script's filename.
exit "$E_WRONG_ARGS"
fi
Invoking the script
Having written the script, you can invoke it by bash scriptname
. Much more convenient is to make the script itself directly executable with a chmod
. Either:
chmod +x scriptname
(gives everyone execute permission) orchmod u+x scriptname
(gives only the script owner execute permission)
Having made the script executable, you may now test it by ./scriptname
. If it begins with a “shebang” line, invoking the script calls the correct command interpreter to run it.
As a final step, after testing and debugging, you would likely want to move it to /usr/local/bin
(as root, of course), to make the script available to yourself and all other users as a systemwide executable. The script could then be invoked by simply typing scriptname
[ENTER] from the command-line.
Notes
- Why not simply invoke the script with
scriptname
? If the directory you are in ($PWD
) is wherescriptname
is located, why doesn’t this work? This fails because, for security reasons, the current directory (./
) is not by default included in a user’s$PATH
. It is therefore necessary to explicitly invoke the script in the current directory with./scriptname
.
From Specific to General
Many times, you’ll write a script for a particular task. Later, you might want to generalize it. Replacing hardcoded values with variables and using functions can make your scripts more flexible and reusable.
This post has given you a taste of basic Bash scripting. In the coming chapters, we’ll delve deeper into variables, control flow, loops, functions, and more. Get ready to level up your scripting skills!
No responses yet