Operations 10 min read

Master Bash Scripting: Safety, Functions, and Best Practices

This article presents a comprehensive guide to writing robust Bash scripts, covering safety options, function definitions, variable annotations, modern syntax like $() and [[ ]], string manipulation techniques, built‑in variables, debugging methods, and recommendations on when to avoid Bash in favor of other languages.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Master Bash Scripting: Safety, Functions, and Best Practices

Script Safety

Start every Bash script with these options to prevent common errors:

#!/bin/bash
set -o nounset
set -o errexit

This avoids referencing undefined variables and silently ignoring failed commands. Note that some commands (e.g., mkdir -p, rm -f) can force error suppression, and errexit does not catch every failure.

Script Functions

Define reusable functions to improve readability. Example:

ExtractBashComments() {
  egrep "^#"
}

cat myscript.sh | ExtractBashComments | wc
comments=$(ExtractBashComments < myscript.sh)

Additional examples:

SumLines() {
  # iterating over stdin - similar to awk
  local sum=0
  local line=""
  while read line; do
    sum=$((sum + line))
  done
  echo $sum
}

SumLines < data_one_number_per_line.txt

log() {
  # classic logger
  local prefix="[$(date +%Y/%m/%d %H:%M:%S)]: "
  echo "${prefix}$@" >&2
}

log "INFO" "a message"

Place only global variables, constants, and the call to main at the top level.

Variable Annotations

Use local for function‑scoped variables and readonly for constants. Example:

# a useful idiom: DEFAULT_VAL can be overwritten
#        with an environment variable of the same name
readonly DEFAULT_VAL=${DEFAULT_VAL:-7}

myfunc() {
  # initialize a local variable with the global default
  local some_var=${DEFAULT_VAL}
  ...
}

Convert a mutable variable to read‑only:

x=5
x=6
readonly x
x=7   # failure

Prefer annotating all variables with local or readonly.

Use $() Instead of Backticks

$()

is clearer and nestable:

# both commands below print out: A-B-C-D
echo "A-`echo B-\`echo C-\\\`echo D\\\`\`"
echo "A-$(echo B-$(echo C-$(echo D)))"

Prefer [[ ]] Over [ ]

Double brackets provide logical operators, string/regex comparisons, and avoid many quoting issues. Examples:

# single brackets
[ "${name}" > "a" -o ${name} < "m" ]

# double brackets
[[ "${name}" > "a" && "${name}" < "m" ]]

Regular Expressions / Globbing

Double brackets enable globbing and regex matching:

t="abc123"
[[ "$t" == abc* ]]   # true (globbing)
[[ "$t" == "abc*" ]] # false (literal)
[[ "$t" =~ [abc]+[123]+ ]] # true (regex)

From Bash 3.2 onward, regexes should not be quoted. Store complex patterns in variables if they contain spaces.

String Operations

Various ways to manipulate strings, including length, slicing, substitution, and removal:

f="path1/path2/file.ext"
len="${#f}"               # length = 20
slice1="${f:6}"           # "path2/file.ext"
slice2="${f:6:5}"         # "path2"
slice3="${f: -8}"        # "file.ext"
slice4="${f:${pos}:${len}}" # using variables

# substitution
single_subst="${f/path?/x}"   # "x/path2/file.ext"
global_subst="${f//path?/x}" # "x/x/file.ext"

# delete prefix/suffix
extension="${f#*.}"   # "ext"
filename="${f##*/}"   # "file.ext"
dirname="${f%/*}"   # "path1/path2"
root="${f%%/*}"       # "path1"

Avoid Temporary Files

Use process substitution and here‑documents when a command expects a filename:

# compare two web pages without temporary files
diff <(wget -O - url1) <(wget -O - url2)

Here‑document example:

command << MARKER
...${var}...
$(cmd)
MARKER

Built‑in Variables

Common special variables:

$0 – script name

$n – nth argument

$$ – script PID

$! – PID of last background command

$? – exit status of last command

$# – number of arguments

$@ – all arguments as separate words

$* – all arguments as a single word (rarely useful)

Prefer $@ with quotes to preserve spacing.

Debugging

Syntax check: bash -n myscript.sh Verbose execution: bash -v myscript.sh Execution tracing with expanded info: bash -x myscript.sh Use set -o verbose and set -o xtrace in the script header for permanent tracing, especially on remote machines.

When Not to Use Bash

Avoid Bash for scripts that are very long, require complex data structures, involve intricate escaping, perform heavy string manipulation, have minimal interaction with other programs, or need high performance. In such cases consider languages like Python or Ruby.

References

Advanced Bash‑Scripting Guide: http://tldp.org/LDP/abs/html/

Bash Reference Manual

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

debuggingbashvariable-annotationsscript safety
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.