Introduction
Bash is the scripting language used to interact directly with Unix‑based operating systems through the shell. It allows administrators and security practitioners to automate repetitive tasks, chain command‑line utilities, and process large volumes of data efficiently. In modern environments, Bash is not limited to Linux systems, as Windows provides compatibility through the Windows Subsystem for Linux, enabling cross‑platform usage.
In enterprise and security‑focused environments, Bash scripting becomes essential due to the scale and velocity of data handled daily. Analysts frequently rely on scripts to enumerate systems, filter logs, test connectivity, and orchestrate reconnaissance or defensive workflows. Instead of manually executing dozens of commands, a single well‑designed script can perform complex logic reliably and repeatably.
It is important to understand that a Bash script does not spawn a new process by default. Instead, it is interpreted and executed line by line by the Bash interpreter itself. This execution model influences how variables, functions, and return codes behave throughout the script.
Components
Conditionals
Conditional execution allows a Bash script to make decisions based on runtime values. Without conditionals, scripts would execute sequentially without regard for context or errors, making them unsuitable for real‑world automation. Conditions enable scripts to react dynamically to user input, command results, or system state.
In Bash, conditionals typically rely on the if, elif, and else keywords combined with test expressions. These expressions evaluate values such as strings, integers, file attributes, or command return codes. Once a condition is met, the associated block executes and the remaining branches are skipped.
Example
#!/bin/bash
if [ $# -lt 1 ]; then
echo "No arguments provided."
exit 1
else
echo "Argument received: $1"
fi
This example demonstrates basic argument validation. The script checks whether at least one argument was supplied and exits gracefully if the condition is not met. This pattern is fundamental for writing safe and predictable scripts.
If‑Else‑Fi‑Elif
The elif keyword allows multiple conditional branches to be evaluated in sequence. This structure is useful when a script must handle several mutually exclusive states or input ranges. Only the first matching condition is executed, ensuring deterministic behavior.
#!/bin/bash
value=$1
if [ "$value" -gt 10 ]; then
echo "Value is greater than 10"
elif [ "$value" -lt 10 ]; then
echo "Value is less than 10"
else
echo "Value equals 10 or is invalid"
fi
This structure is commonly used when validating numeric input, selecting configuration paths, or implementing thresholds in monitoring scripts.
Arguments
Bash allows up to nine positional arguments to be accessed directly without explicit declaration. These arguments are referenced using $1 through $9, while $0 refers to the script name itself. This mechanism simplifies script invocation and parameter handling.
./scan.sh target.com 80 tcp
# $0 = scan.sh
# $1 = target.com
# $2 = 80
# $3 = tcp
Arguments are parsed based on whitespace and the internal field separator. Proper quoting is essential to avoid unexpected behavior when arguments contain spaces or special characters.
Special Variables
Bash provides special variables that expose execution context and command results. These variables are frequently used for flow control, debugging, and error handling.
echo "Arguments count: $#"
echo "All arguments: $@"
echo "Script PID: $$"
echo "Last exit code: $?"
These values allow scripts to introspect their own execution and react accordingly.
Variable Declaration
Variables in Bash are dynamically typed and treated as strings by default. Assignment does not allow spaces around the equals sign, and variable expansion requires the $ prefix.
message="Execution completed successfully"
echo "$message"
Despite their simplicity, variables are powerful when combined with command substitution and arithmetic expansion.
Arrays
Arrays allow multiple values to be stored under a single variable name. They are particularly useful when iterating over lists of hosts, ports, or filenames.
domains=(example.com test.local internal.lan)
for d in "${domains[@]}"; do
echo "Resolving $d"
done
Each array element is indexed starting at zero, and iteration preserves element boundaries when quoted correctly.
Comparators
Comparison operators define how values are evaluated within conditions. Bash differentiates between string, integer, file, and logical comparisons, each with its own syntax and semantics.
String Operators
if [ -z "$var" ]; then
echo "Variable is empty"
fi
String comparisons are commonly used for input validation and configuration checks.
Integer Operators
if [ "$count" -ge 5 ]; then
echo "Threshold reached"
fi
Integer operators enable numerical logic such as counters, limits, and thresholds.
File Operators
if [ -f "/etc/passwd" ]; then
echo "File exists"
fi
File operators are essential for scripts that manage configuration files or system resources.
Logical Operators
if [ -f "$file" ] && [ -r "$file" ]; then
echo "File is readable"
fi
Logical operators allow compound conditions, increasing expressiveness while maintaining clarity.
Arithmetic
Bash supports arithmetic expansion for integer calculations. This functionality is commonly used for counters, loop control, and simple math.
count=0
((count++))
echo "Count: $count"
Arithmetic expansion avoids external utilities and improves performance in tight loops.
Script Control
Input and Output
User interaction is often required in scripts that support multiple execution paths. Bash provides the read command to capture user input and echo to display output.
read -p "Enter a hostname: " host
echo "You entered: $host"
This mechanism allows scripts to behave interactively while still supporting automation.
Loops
Loops enable repetitive execution of code blocks. Bash supports for, while, and until loops, each suited to different scenarios.
For
for i in {1..5}; do
echo "Iteration $i"
done
The for loop is ideal for iterating over known sequences or lists.
While
count=0
while [ $count -lt 3 ]; do
echo "Count: $count"
((count++))
done
While loops execute as long as a condition remains true.
Until
count=0
until [ $count -eq 5 ]; do
echo "Count: $count"
((count++))
done
Until loops invert the condition logic and run until a condition becomes true.
Branches – Switch‑Case
case $1 in
start) echo "Starting service" ;;
stop) echo "Stopping service" ;;
*) echo "Unknown option" ;;
esac
Case statements provide a clean alternative to deeply nested conditionals.
Execution
Functions
Functions group reusable logic under a single name, improving readability and maintainability. They are executed in the same shell context unless otherwise specified.
greet() {
echo "Hello, $1"
}
greet "Alice"
Functions support parameters and return status codes, enabling modular script design.
Debugging
Debugging Bash scripts involves tracing command execution and variable expansion. The -x and -v flags instruct Bash to print commands as they are executed.
bash -x script.sh
bash -x -v script.sh
These options are invaluable when diagnosing logic errors, unexpected variable values, or execution flow issues.
Script Example
The following script demonstrates argument handling, DNS resolution, network enumeration, host discovery, and user interaction. It represents a realistic Bash utility used during reconnaissance or infrastructure analysis.
#!/bin/bash
# Check for given arguments
if [ $# -eq 0 ]; then
echo "You must specify a target domain."
echo "Usage: $0 <domain>"
exit 1
else
domain=$1
fi
# Identify IP address of the specified domain
hosts=$(host "$domain" | grep "has address" | awk '{print $4}')
ipaddr=$(echo "$hosts" | tr "\n" " ")
# Identify network range
network_range() {
for ip in $ipaddr; do
whois "$ip" | grep -E "NetRange|CIDR" | tee -a CIDR.txt
done
}
# Ping discovered IP addresses
ping_hosts() {
for host in $ipaddr; do
ping -c 2 "$host" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "[+] $host is reachable"
else
echo "[-] $host is unreachable"
fi
done
}
# Menu
echo "1) Show network range"
echo "2) Ping discovered hosts"
echo "3) Run all checks"
echo "*) Exit"
read -p "Select an option: " opt
case $opt in
1) network_range ;;
2) ping_hosts ;;
3) network_range && ping_hosts ;;
*) exit 0 ;;
esac
This script combines multiple Bash concepts into a single workflow. It validates input, processes command output, stores intermediate values, and exposes functionality through a simple menu. Each section can be extended or reused independently.
Reference
Based on personal study notes and practical scripting experience.
Repository: https://github.com/lameiro0x/security-foundations-htb-notes