Bash Scripting Examples - Command Line Utilities

Explore essential Bash scripting examples for command-line utilities, including loops, case statements, debugging, file operations, and variable expansion. Enhance your shell scripting skills.

Bash Scripting Examples

This section provides practical examples of Bash scripting for common command-line tasks and utilities. Mastering these techniques can significantly improve your efficiency when working in Linux and Unix-like environments.

Bash For Loops

The for loop is fundamental for iterating over a list of items. Here's how to implement it:

# To implement a for loop:
for WORD in LIST
do
    COMMANDS
done
# For example:
for CurDay in Monday Tuesday Wednesday Thursday Friday Saturday Sunday
do
    printf "%s\n" "$CurDay"
done

Bash Case Statements

case statements are useful for pattern matching and executing different commands based on input:

# To implement a case statement:
case $1 in
    0)
        echo "Found a '0'." ;;
    1)
        echo "Found a '1'." ;;
    2)
        echo "Found a '2'." ;;
    3*)
        echo "Something beginning with '3' found." ;;
    '')
        echo "Nothing (null) found." ;;
    *)
        echo "Anything else found." ;;
esac

Bash Debugging

Enable verbose output to debug your scripts:

# Turn on built-in Bash debugging output:
set -x
# Turn the above off again:
set +x

Managing Piped Command Exit Status

Retrieve the exit status of commands within a pipeline:

# Retrieve N-th piped command exit status
printf 'foo' | grep -F 'foo' | sed 's/foo/bar/'
echo ${PIPESTATUS[0]}  # replace 0 with N

File Locking in Bash

Implement a simple file lock to prevent concurrent execution:

# Lock file:
( set -o noclobber; echo > my.lock ) || echo 'Failed to create lock file'

Dangerous Bash Examples (Use with Extreme Caution)

The following examples are provided for educational purposes only and should NEVER be run on a production system or without a full understanding of their consequences.

# Fork bomb. Do not run this! Has the potential to wreak havoc. It repeatedly
# and quickly spawns a lot of processes until the system eventually locks up.
:(){ :|:& };:
# An alternative, easier-to-understand version without the obfuscation:
func(){ func | func & }; func

# Unix Roulette, courtesy of Bigown's answer in the joke thread.
#
#   DANGER! Don't execute!
#
# Luckily, most modern setups have `--preserve-root` on by default, so this
# will be null and void, but even so, not even remotely worth the risk!
[ $[ $RANDOM % 6 ] == 0 ] && rm -rf /* || echo Click #Roulette

Bash Loop One-Liners

Concise ways to write loops:

# A for loop one-liner.
for CurIter in {1..4}; do echo "$CurIter"; done
# Alternative, slightly-cleaner syntax:
for CurIter in {1..4}; { echo "$CurIter"; }

Conditional Tests in Bash

Perform checks on variables and conditions:

# Test for a variable being equal to (`-eq`) an integer (`0`).
if [ $var -eq 0 ]; then
    printf "Variable '\$var' is equal to '0'.\n"
fi

# Test for a `PATH` executable existing as a file, but note that aliases and
# functions will also output and result in a `0` exit status.
command -v ${program} >/dev/null 2>&1 || error "${program} not installed"
# However, that is a solution commonly found in a script using the Bourne
# shell, so in this case, an alternative, Bash-like, and more accurate version
# could be:
if ! type -fP bash > /dev/null 2>&1; then
    printf "ERROR: Dependency 'bash' not met." >&2
    exit 1
fi

Input/Output Redirection and Piping

Control where command output goes and how commands are chained:

# Send both STDOUT and STDERR from COMMAND to FILE.
COMMAND > FILE 2>&1
# Send STDOUT and STDERR from COMMAND to `/dev/null`, where data goes to die.
COMMAND > /dev/null 2>&1
# Pipe the STDOUT and STDERR from COMMAND_1 to COMMAND_2.
COMMAND_1 |& COMMAND_2

File Name Manipulation

Common tasks for renaming files:

# Verbosely convert whitespaces (` `) to underscores (`_`) in file names.
for name in *\ *; do mv -vn "$name" "${name// /_}"; done

Bash Variable Expansion

Understand how to access and manipulate variables:

# Expand a regular variable.
$Var
# Some people like to cuddle the variable name with braces ('{' and '}') but
# this is usually superfluous and wasted keystrokes, unless you need to protect
# the variable name from having other characters included, as in the below
# example, or you're using one of the many features of parameter expansion.
"${Var}some text"

# Access a given index in an array. In this example, you don't technically
# need to specify the element, because by default the first element is used.
# As with many other languages, note that indices are 0-first, so 1 is the 2nd.
${Var[0]}
# You can use arithmetic between '[' and ']' as well.
${Var[2+3]}

# Expand variable to the length of that to which the variable would expand.
${#Var}

# Expand array variable to the number of elements/indices. You may find that
# `[*]` works as well as `[@]`, in this case.
${#Var[@]}

# Expand variable to a substring. In this case, imagine `Var` is equal to the
# string 'thing', but the offset is 2 and the length is 1, giving us an 'i'.
${Var:2:1}

# Expand variable to a substring by removing the matched glob pattern from left
# to right. To make this global (IE: greedy) use two '#' characters. So, in
# this example, everything, from left to right, up to and including a 'T' or
# 't', will be removed, but it would only happen once.
${Var#*[Tt]}
# As above, but from right to left. Use two '%' characters for a greedy match.
${Var%[Tt]*}

# Change how the variable expands by using pattern substitution. This uses glob
# pattern matching, not REGEX. If the first '/' is instead '//', a greedy match
# is performed.
${Var/PATTERN/REPLACEMENT/}
# A good example of the above, which will list directories in PATH which exist
# and are directories. It works because all instances of ':' are replaced with
# a whitespace, causing find(1) to see multiple directories (fields) in which
# to search.
find ${PATH//:/ } -type d

# Expand the variable to the string between `:-` and the closing `}`, if that
# variable doesn't already have a value. This can be useful to set a default.
${Var:-Default Value}

# This is a way of displaying an error message if the contents of the variable
# is empty. It will also immediately exit with an exit status of 1.
${Var:?This is an error message.}

# Indirect expansion exists in a couple of ways in BASH. If `Var` is equal to
# `OtherVar`, and that `OtherVar` is equal to `true`, the below example would
# expand to `true`.
${!Var}

# Expand variable so that the first letter is uppercase. Use two '^' (carets)
# if you want the entire contents of the variable to change to uppercase.
${Var^}
# As above, but convert to lowercase. Use two ',' characters to transform the
# entire string to which `Var` expands.
${Var,}

Further Reading