Bash Scripting for Hackers: Automation Basics (2026 Complete Guide)

# Bash Scripting for Hackers: Automation Basics (2026 Complete Guide)

## Introduction

In the world of penetration testing and ethical hacking, **bash scripting** is one of the most powerful skills you can develop. While manual commands work for one-off tasks, automation through bash scripts enables you to perform complex operations efficiently, consistently, and at scale.

Whether you're conducting reconnaissance, analyzing logs, brute-forcing credentials, or enumerating targets, bash scripting transforms repetitive tasks into executable workflows. This tutorial will teach you the fundamentals of bash scripting specifically tailored for cybersecurity professionals and ethical hackers.

**Prerequisites**: Basic [Linux terminal knowledge](/tutorials/linux-terminal-mastery-command-line-for-beginners-2026-guide) and familiarity with [essential Linux commands](/tutorials/50-essential-linux-commands-for-cybersecurity-and-ethical-hacking-2026-guide).

---

## Table of Contents

1. [Why Bash Scripting for Penetration Testers](#why-bash-scripting)
2. [Bash Script Anatomy](#bash-script-anatomy)
3. [Variables in Bash](#variables-in-bash)
4. [Conditionals (if/else/case)](#conditionals)
5. [Loops (for/while/until)](#loops)
6. [Functions](#functions)
7. [Arrays](#arrays)
8. [String Manipulation](#string-manipulation)
9. [File I/O Operations](#file-io-operations)
10. [Command Substitution](#command-substitution)
11. [Practical Pentesting Scripts](#practical-pentesting-scripts)
12. [Error Handling](#error-handling)
13. [Scheduling with Cron](#scheduling-with-cron)
14. [Manual vs Scripted Workflow Comparison](#comparison-table)
15. [FAQ](#faq)

---

## Why Bash Scripting for Penetration Testers? {#why-bash-scripting}

### The Power of Automation

Bash scripting enables penetration testers to:

✅ **Automate Repetitive Tasks** — Eliminate manual execution of the same commands across multiple targets
✅ **Increase Efficiency** — Run complex multi-stage attacks with a single command
✅ **Ensure Consistency** — Execute tests the same way every time, reducing human error
✅ **Scale Operations** — Process hundreds or thousands of targets simultaneously
✅ **Create Custom Tools** — Build specialized utilities tailored to your specific needs
✅ **Chain Multiple Tools** — Integrate Nmap, Metasploit, Burp Suite, and other tools seamlessly
✅ **Schedule Reconnaissance** — Set up automated cron jobs for continuous monitoring

### Real-World Use Cases

- **Automated port scanning** across entire network ranges
- **Subdomain enumeration** with multiple tools and consolidation
- **Log parsing and analysis** to identify attack patterns
- **Credential validation** against multiple services
- **Report generation** with formatted output
- **Continuous vulnerability monitoring** of target infrastructure

Every professional penetration tester uses bash scripting daily. It's the glue that connects individual tools into powerful automated workflows.

---

## Bash Script Anatomy {#bash-script-anatomy}

### The Shebang Line

Every bash script starts with a **shebang** (`#!`) that tells the system which interpreter to use:

```bash
#!/bin/bash
```

This line must be the **first line** of your script. It specifies the absolute path to the bash interpreter.

**Alternative shebangs:**

```bash
#!/usr/bin/env bash # More portable, finds bash in PATH
#!/bin/sh # POSIX shell (less features)
```

### Basic Script Structure

```bash
#!/bin/bash

# Script: basic_template.sh
# Description: Template for bash scripts
# Author: Andrax Pentester
# Date: 2026

# Variables
TARGET="192.168.1.1"
PORT=80

# Main logic
echo "Scanning target: $TARGET"
echo "Port: $PORT"

# Exit with status code
exit 0
```

### Making Scripts Executable

After creating a script, make it executable:

```bash
chmod +x script.sh
./script.sh
```

---

## Variables in Bash {#variables-in-bash}

### Local Variables

Variables store data for use throughout your script.

```bash
#!/bin/bash

# Variable assignment (no spaces around =)
target="example.com"
port=443
protocol="https"

# Using variables (prefix with $)
echo "Target: $target"
echo "URL: ${protocol}://${target}:${port}"
```

**Best practices:**
- No spaces around the `=` sign
- Use `${variable}` for clarity and to prevent ambiguity
- Quote variables to handle spaces: `"$variable"`

### Environment Variables

Environment variables are available system-wide:

```bash
#!/bin/bash

# Read environment variables
echo "Current user: $USER"
echo "Home directory: $HOME"
echo "Current path: $PATH"

# Set environment variable for child processes
export API_KEY="your-api-key-here"
```

### Positional Parameters (Command-Line Arguments)

Capture arguments passed to your script:

```bash
#!/bin/bash

# $0 = script name
# $1, $2, $3... = arguments
# $# = number of arguments
# $@ = all arguments as separate words
# $* = all arguments as a single word

echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Total arguments: $#"
echo "All arguments: $@"
```

**Usage:**

```bash
./script.sh 192.168.1.1 80 tcp
# $1 = 192.168.1.1
# $2 = 80
# $3 = tcp
```

### Special Variables

```bash
$? # Exit status of last command (0 = success)
$$ # Process ID of current script
$! # Process ID of last background command
```

---

## Conditionals (if/else/case) {#conditionals}

### if/elif/else Statements

```bash
#!/bin/bash

port=$1

if [ -z "$port" ]; then
echo "Error: No port specified"
exit 1
elif [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
echo "Error: Port must be between 1-65535"
exit 1
else
echo "Valid port: $port"
fi
```

### Test Operators

**Numeric comparisons:**
```bash
-eq # Equal to
-ne # Not equal to
-lt # Less than
-le # Less than or equal to
-gt # Greater than
-ge # Greater than or equal to
```

**String comparisons:**
```bash
= # Equal to
!= # Not equal to
-z # String is empty
-n # String is not empty
```

**File tests:**
```bash
-e # File exists
-f # File is a regular file
-d # File is a directory
-r # File is readable
-w # File is writable
-x # File is executable
-s # File is not empty
```

### case Statements

For multiple conditions:

```bash
#!/bin/bash

service=$1

case $service in
ssh)
port=22
;;
http)
port=80
;;
https)
port=443
;;
mysql)
port=3306
;;
*)
echo "Unknown service: $service"
exit 1
;;
esac

echo "$service runs on port $port"
```

---

## Loops (for/while/until) {#loops}

### for Loops

**Iterate over a list:**

```bash
#!/bin/bash

# Loop through IP addresses
for ip in 192.168.1.1 192.168.1.2 192.168.1.3; do
echo "Scanning $ip"
ping -c 1 "$ip" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "$ip is alive"
fi
done
```

**Range-based loop:**

```bash
#!/bin/bash

# Scan ports 1-100
for port in {1..100}; do
timeout 1 bash -c "echo > /dev/tcp/192.168.1.1/$port" 2>/dev/null
if [ $? -eq 0 ]; then
echo "Port $port is open"
fi
done
```

**C-style loop:**

```bash
#!/bin/bash

for ((i=1; i<=10; i++)); do
echo "Attempt $i"
done
```

### while Loops

Execute while condition is true:

```bash
#!/bin/bash

count=1

while [ $count -le 5 ]; do
echo "Try $count"
((count++))
done
```

**Reading files line by line:**

```bash
#!/bin/bash

while IFS= read -r line; do
echo "Processing: $line"
done < targets.txt
```

### until Loops

Execute until condition becomes true:

```bash
#!/bin/bash

count=1

until [ $count -gt 5 ]; do
echo "Count: $count"
((count++))
done
```

---

## Functions {#functions}

Functions organize reusable code blocks.

### Basic Function Syntax

```bash
#!/bin/bash

# Define function
check_host() {
local host=$1
ping -c 1 "$host" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "$host is reachable"
return 0
else
echo "$host is unreachable"
return 1
fi
}

# Call function
check_host "google.com"
check_host "192.168.1.1"
```

### Functions with Return Values

```bash
#!/bin/bash

port_scan() {
local target=$1
local port=$2

timeout 1 bash -c "echo > /dev/tcp/$target/$port" 2>/dev/null
return $?
}

if port_scan "192.168.1.1" 80; then
echo "Port 80 is open"
else
echo "Port 80 is closed"
fi
```

---

## Arrays {#arrays}

Arrays store multiple values in a single variable.

### Indexed Arrays

```bash
#!/bin/bash

# Declare array
targets=("192.168.1.1" "192.168.1.2" "192.168.1.3")

# Access elements
echo "First target: ${targets[0]}"
echo "Second target: ${targets[1]}"

# Array length
echo "Total targets: ${#targets[@]}"

# Iterate over array
for target in "${targets[@]}"; do
echo "Scanning $target"
done

# Add element
targets+=("192.168.1.4")

# All elements
echo "All targets: ${targets[@]}"
```

### Associative Arrays (Bash 4.0+)

```bash
#!/bin/bash

# Declare associative array
declare -A ports
ports[ssh]=22
ports[http]=80
ports[https]=443
ports[mysql]=3306

# Access by key
echo "SSH port: ${ports[ssh]}"

# Iterate over keys
for service in "${!ports[@]}"; do
echo "$service runs on port ${ports[$service]}"
done
```

---

## String Manipulation {#string-manipulation}

### String Length

```bash
string="example.com"
echo ${#string} # Output: 11
```

### Substring Extraction

```bash
url="https://example.com/path"
echo ${url:8:11} # Output: example.com (start at position 8, length 11)
```

### Replace Substring

```bash
path="/var/log/apache2/access.log"
echo ${path/apache2/nginx} # Replace first occurrence
echo ${path//\//-} # Replace all occurrences of / with -
```

### Remove Prefix/Suffix

```bash
filename="report.txt"

# Remove extension
echo ${filename%.txt} # Output: report

# Extract extension
echo ${filename##*.} # Output: txt
```

### Case Conversion (Bash 4.0+)

```bash
string="Example Domain"

echo ${string,,} # Lowercase: example domain
echo ${string^^} # Uppercase: EXAMPLE DOMAIN
```

---

## File I/O Operations {#file-io-operations}

### Reading Files

```bash
#!/bin/bash

# Read entire file
content=$(cat targets.txt)

# Read line by line
while IFS= read -r line; do
echo "Processing: $line"
done < targets.txt
```

### Writing to Files

```bash
#!/bin/bash

# Overwrite file
echo "New content" > output.txt

# Append to file
echo "Additional line" >> output.txt

# Write multiple lines
cat > results.txt << EOF
Scan Results
============
Target: example.com
Status: Complete
EOF
```

### File Existence Checks

```bash
#!/bin/bash

if [ -f "targets.txt" ]; then
echo "File exists"
while read -r target; do
echo "Target: $target"
done < targets.txt
else
echo "Error: targets.txt not found"
exit 1
fi
```

---

## Command Substitution {#command-substitution}

Capture command output and store it in variables.

### Syntax

```bash
# Modern syntax (preferred)
result=$(command)

# Old syntax (deprecated)
result=`command`
```

### Examples

```bash
#!/bin/bash

# Get current date
today=$(date +%Y-%m-%d)
echo "Today: $today"

# Count files
file_count=$(ls -1 | wc -l)
echo "Files: $file_count"

# Store command output
open_ports=$(nmap -p- --open 192.168.1.1 | grep "open")
echo "$open_ports"

# Use in conditionals
if [ $(whoami) = "root" ]; then
echo "Running as root"
else
echo "Not root"
fi
```

---

## Practical Pentesting Scripts {#practical-pentesting-scripts}

### 1. TCP Port Scanner

```bash
#!/bin/bash

# File: port_scanner.sh
# Description: Simple TCP port scanner using /dev/tcp

if [ $# -ne 3 ]; then
echo "Usage: $0 <target> <start_port> <end_port>"
exit 1
fi

target=$1
start_port=$2
end_port=$3

echo "[*] Scanning $target from port $start_port to $end_port"
echo "[*] Started at $(date)"
echo ""

for ((port=start_port; port<=end_port; port++)); do
timeout 1 bash -c "echo > /dev/tcp/$target/$port" 2>/dev/null
if [ $? -eq 0 ]; then
echo "[+] Port $port is OPEN"
fi
done

echo ""
echo "[*] Scan completed at $(date)"
```

**Usage:**
```bash
chmod +x port_scanner.sh
./port_scanner.sh 192.168.1.1 1 1000
```

### 2. Subdomain Enumerator

```bash
#!/bin/bash

# File: subdomain_enum.sh
# Description: Enumerate subdomains using wordlist

if [ $# -ne 2 ]; then
echo "Usage: $0 <domain> <wordlist>"
exit 1
fi

domain=$1
wordlist=$2
output="subdomains_${domain}.txt"

if [ ! -f "$wordlist" ]; then
echo "Error: Wordlist $wordlist not found"
exit 1
fi

echo "[*] Enumerating subdomains for $domain"
echo "[*] Using wordlist: $wordlist"
echo ""

> "$output" # Clear output file

while IFS= read -r subdomain; do
full_domain="${subdomain}.${domain}"

# Try DNS resolution
if host "$full_domain" > /dev/null 2>&1; then
echo "[+] Found: $full_domain"
echo "$full_domain" >> "$output"
fi
done < "$wordlist"

echo ""
echo "[*] Results saved to $output"
echo "[*] Total found: $(wc -l < "$output")"
```

**Usage:**
```bash
./subdomain_enum.sh example.com /usr/share/wordlists/subdomains.txt
```

### 3. Log Parser for Failed SSH Attempts

```bash
#!/bin/bash

# File: ssh_log_parser.sh
# Description: Parse SSH logs for failed login attempts

log_file="/var/log/auth.log"
output="failed_ssh_attempts.txt"

if [ ! -r "$log_file" ]; then
echo "Error: Cannot read $log_file (run as root)"
exit 1
fi

echo "[*] Parsing SSH failed login attempts"
echo ""

# Extract failed password attempts
grep "Failed password" "$log_file" | \
awk '{print $1, $2, $3, $11, $13}' | \
sort | uniq -c | sort -rn > "$output"

echo "[*] Top 10 attacking IPs:"
echo ""

head -10 "$output"

echo ""
echo "[*] Full report saved to $output"
echo "[*] Total unique attempts: $(wc -l < "$output")"
```

**Usage:**
```bash
sudo ./ssh_log_parser.sh
```

### 4. Credential Checker

```bash
#!/bin/bash

# File: credential_checker.sh
# Description: Test credentials against SSH service

if [ $# -ne 3 ]; then
echo "Usage: $0 <target> <username_file> <password_file>"
exit 1
fi

target=$1
username_file=$2
password_file=$3

if [ ! -f "$username_file" ] || [ ! -f "$password_file" ]; then
echo "Error: Username or password file not found"
exit 1
fi

echo "[*] Testing credentials against $target"
echo "[*] WARNING: Use only with permission!"
echo ""

while IFS= read -r username; do
while IFS= read -r password; do
echo -n "[*] Testing ${username}:${password}... "

# Use sshpass with timeout
timeout 5 sshpass -p "$password" ssh -o StrictHostKeyChecking=no \
-o ConnectTimeout=3 "${username}@${target}" "exit" 2>/dev/null

if [ $? -eq 0 ]; then
echo "SUCCESS!"
echo "[+] Valid credentials: ${username}:${password}" >> valid_creds.txt
else
echo "Failed"
fi

sleep 1 # Rate limiting
done < "$password_file"
done < "$username_file"

echo ""
echo "[*] Scan complete. Check valid_creds.txt for results."
```

**Note:** This is for educational purposes only. Always obtain proper authorization before testing.

---

## Error Handling {#error-handling}

### Exit Codes

```bash
#!/bin/bash

# Exit immediately on error
set -e

# Exit on undefined variable
set -u

# Pipe failure detection
set -o pipefail

# Combined
set -euo pipefail
```

### Error Checking

```bash
#!/bin/bash

command_with_error() {
nmap -sV 192.168.1.1 -oN scan.txt

if [ $? -ne 0 ]; then
echo "Error: Nmap scan failed"
return 1
fi

return 0
}

if command_with_error; then
echo "Success"
else
echo "Failed"
exit 1
fi
```

### Trap for Cleanup

```bash
#!/bin/bash

# Cleanup function
cleanup() {
echo "Cleaning up temporary files..."
rm -f /tmp/scan_results_*.txt
echo "Done"
}

# Execute cleanup on exit
trap cleanup EXIT

# Main script
echo "Running scan..."
nmap 192.168.1.1 > /tmp/scan_results_$(date +%s).txt
```

---

## Scheduling with Cron {#scheduling-with-cron}

### Cron Syntax

```
* * * * * command
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, Sunday=0 or 7)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
```

### Scheduling Examples

```bash
# Edit crontab
crontab -e

# Run script every day at 2 AM
0 2 * * * /root/scripts/daily_scan.sh

# Run every hour
0 * * * * /root/scripts/hourly_check.sh

# Run every 15 minutes
*/15 * * * * /root/scripts/quick_scan.sh

# Run Monday-Friday at 9 AM
0 9 * * 1-5 /root/scripts/weekday_scan.sh

# Run on the 1st of every month
0 0 1 * * /root/scripts/monthly_report.sh
```

### Automated Reconnaissance Script

```bash
#!/bin/bash

# File: automated_recon.sh
# Description: Daily reconnaissance automation

target="example.com"
date_stamp=$(date +%Y-%m-%d)
output_dir="/root/recon/$target/$date_stamp"

mkdir -p "$output_dir"

echo "[*] Starting automated recon for $target"

# Subdomain enumeration
subfinder -d "$target" -o "$output_dir/subdomains.txt" 2>&1

# Port scanning
nmap -p- --open "$target" -oN "$output_dir/nmap_scan.txt" 2>&1

# Technology detection
whatweb "$target" > "$output_dir/whatweb.txt" 2>&1

echo "[*] Recon complete. Results in $output_dir"
```

**Add to crontab:**
```bash
0 3 * * * /root/scripts/automated_recon.sh >> /var/log/recon.log 2>&1
```

---

## Manual vs Scripted Workflow Comparison {#comparison-table}

| Task | Manual Approach | Scripted Approach | Time Saved |
|------|----------------|-------------------|------------|
| **Port Scan 10 hosts** | Run nmap 10 times, type each IP | `for ip in hosts; do nmap $ip; done` | 90% |
| **Subdomain Enumeration** | Test each subdomain manually | Automated wordlist iteration | 95% |
| **Log Analysis** | Read logs line by line | Grep + awk parsing with sorting | 98% |
| **Credential Testing** | Type each user/pass combo | Loop through credential files | 99% |
| **Daily Reconnaissance** | Login daily and run commands | Cron-scheduled automation | 100% |
| **Multi-tool Workflow** | Run each tool separately | Chain tools with pipes and substitution | 85% |
| **Report Generation** | Copy/paste results manually | Automated formatting and file output | 90% |
| **Error Handling** | Notice and fix errors manually | Automatic error detection and logging | 80% |

**Key Insight:** Scripting transforms hours of manual work into seconds of automated execution, allowing you to focus on analysis rather than repetitive tasks.

---

## Frequently Asked Questions {#faq}

### 1. What's the difference between `#!/bin/bash` and `#!/bin/sh`?

- **`#!/bin/bash`** — Uses the full-featured Bash shell with arrays, functions, and advanced features
- **`#!/bin/sh`** — Uses the minimal POSIX shell with limited functionality but better portability

For penetration testing scripts, use `#!/bin/bash` for access to all features. If you need maximum portability across different Unix systems, use `#!/bin/sh` or `#!/usr/bin/env bash`.

### 2. How do I debug bash scripts?

Use these debugging techniques:

```bash
# Enable debug mode (print each command)
bash -x script.sh

# Or add to script:
set -x # Enable debugging
set +x # Disable debugging

# Verbose mode
set -v

# Check syntax without running
bash -n script.sh
```

**ShellCheck** is an excellent static analysis tool:

```bash
sudo apt install shellcheck
shellcheck script.sh
```

### 3. Can bash scripts replace Python for pentesting?

Both have their place:

**Use Bash when:**
- Chaining existing tools (Nmap, Metasploit, etc.)
- File and text processing
- Quick automation tasks
- System administration
- Running on minimal systems without Python

**Use Python when:**
- Complex data structures needed
- Network protocol implementation
- GUI applications
- Cross-platform compatibility
- Machine learning / advanced algorithms

Most professional pentesters use **both**: Bash for glue scripts and Python for custom tools.

### 4. How can I make my bash scripts faster?

**Optimization techniques:**

```bash
# Use GNU Parallel for concurrent execution
cat targets.txt | parallel -j 10 nmap -Pn {}

# Minimize subshells (slow)
count=$(cat file.txt | wc -l) # Bad
count=$(wc -l < file.txt) # Good

# Use built-in commands over external
echo ${#string} # Good
echo $string | wc -c # Bad (spawns process)

# Background processes for parallel tasks
for target in $targets; do
scan_target "$target" &
done
wait # Wait for all background jobs
```

### 5. How do I securely handle credentials in scripts?

**Best practices:**

```bash
# Never hardcode credentials
# BAD:
password="admin123"

# GOOD: Read from secure file
if [ -f "$HOME/.credentials" ]; then
source "$HOME/.credentials"
chmod 600 "$HOME/.credentials" # Restrict permissions
fi

# Or prompt user
read -sp "Enter password: " password
echo

# Or use environment variables
export API_KEY="your-key"

# Clear sensitive variables after use
unset password
unset API_KEY
```

**Never commit credentials to git repositories!**

---

## Conclusion

Bash scripting is an essential skill for every penetration tester and ethical hacker. By mastering variables, conditionals, loops, functions, and file operations, you can automate complex workflows and dramatically increase your efficiency.

This tutorial covered:

✅ Bash script fundamentals (shebang, variables, arguments)
✅ Control structures (if/else, case, loops)
✅ Functions and arrays for organized code
✅ String manipulation and file I/O
✅ Command substitution for integrating tools
✅ Practical pentesting automation scripts
✅ Error handling and scheduling

**Next Steps:**

1. Practice writing your own automation scripts
2. Explore [Kali Linux setup and configuration](/tutorials/kali-linux-setup-essential-post-installation-steps-2026)
3. Integrate bash scripts with [Nmap workflows](/articles/nmap-cheat-sheet-complete-command-reference-2026)
4. Study the [GNU Bash manual](https://www.gnu.org/software/bash/manual/)
5. Check [official Kali documentation](https://www.kali.org/docs/)

Remember: **Automation is the key to scaling your penetration testing operations.** Start small, build your script library, and continuously refine your workflows.

**Happy scripting, and stay ethical!**

---

*Tutorial 16 of 105 in the Kali Linux Complete Security Series*
*Author: Andrax Pentester / Syed Abrar*
*Last Updated: 2026*
*Estimated Reading Time: 18 minutes*

---

**Related Tutorials:**

- [Linux Terminal Mastery: Command Line for Beginners](/tutorials/linux-terminal-mastery-command-line-for-beginners-2026-guide)
- [50 Essential Linux Commands for Cybersecurity](/tutorials/50-essential-linux-commands-for-cybersecurity-and-ethical-hacking-2026-guide)
- [Kali Linux Setup: Essential Post-Installation Steps](/tutorials/kali-linux-setup-essential-post-installation-steps-2026)
- [Nmap Cheat Sheet: Complete Command Reference](/articles/nmap-cheat-sheet-complete-command-reference-2026)

**Follow Andrax Pentester for more cybersecurity tutorials, tools, and ethical hacking guides.**