Your first bash script
You've learned commands. Now let's chain them into a script — a file the shell runs top to bottom. This is the moment "using Linux" becomes "automating Linux," and it's genuinely a superpower.
What a script is
A shell script is a text file containing commands, plus a first line that tells Linux which interpreter should run them. For Bash:
#!/bin/bash
# This line above is called a "shebang".
echo "Hello from your first script!"
Save that as hello.sh, make it executable, run it:
chmod +x hello.sh
./hello.sh
That's it — you wrote and ran a script.
Variables
name="Sam"
echo "Hi, $name"
count=5
echo "You have $count files"
# Backticks or $() run a command and use its output:
today=$(date +%Y-%m-%d)
echo "Today is $today"
# Prompting for input:
read -p "Enter a target IP: " target
echo "You entered $target"
Reading command-line arguments
Real scripts take input directly from how they're invoked, not just interactive prompts — genuinely essential once you start building the recon and automation tools referenced throughout Path 04 and Path 15:
#!/bin/bash
# Usage: ./scan.sh target.com
target=$1 # the first argument
echo "Scanning $target..."
echo "Total arguments given: $#"
echo "All arguments: $@"
Run it as ./scan.sh target.com and $1 holds target.com automatically — this is exactly how every command-line security tool you've used so far (nmap, dig, curl) accepts a target, and now you can build tools that work the same way.
Conditions
#!/bin/bash
read -p "Enter a number: " n
if [ "$n" -gt 100 ]; then
echo "Big number"
elif [ "$n" -gt 10 ]; then
echo "Medium"
else
echo "Small"
fi
Common test operators:
| Test | Meaning |
|---|---|
-eq / -ne |
equal / not equal (numbers) |
-gt / -lt / -ge / -le |
greater / less / … or equal (numbers) |
= / != |
string equal / not equal |
-z |
string is empty |
-n |
string is non-empty |
-f file |
file exists |
-d dir |
directory exists |
-r/-w/-x |
readable / writable / executable |
Loops
# For each item:
for host in scanme.nmap.org example.com; do
echo "Pinging $host..."
ping -c 1 "$host" > /dev/null && echo "up" || echo "down"
done
# Read a file line by line:
while read -r line; do
echo "Line: $line"
done < hosts.txt
# Classic counter:
for i in {1..10}; do
echo "Attempt $i"
done
A tiny real security script — port sweep
Here's a script that checks which of a small list of hosts responds on port 80. This is the kind of one-off tool you'll write constantly.
#!/bin/bash
# port-sweep.sh — quick check port 80 on a hosts file
if [ ! -f "$1" ]; then
echo "Usage: $0 hosts.txt"
exit 1
fi
while read -r host; do
if timeout 2 bash -c "echo > /dev/tcp/$host/80" 2>/dev/null; then
echo "[+] $host port 80 open"
else
echo "[-] $host closed/filtered"
fi
done < "$1"
Save, chmod +x port-sweep.sh, make a hosts.txt with a few names, and run:
./port-sweep.sh hosts.txt
You just built a mini port scanner in ten lines of Bash. This is exactly the kind of throwaway tool professionals write daily.
Functions
banner() {
echo "==============================="
echo " $1"
echo "==============================="
}
banner "Recon starting"
# do stuff
banner "Recon complete"
Exit codes
Every command returns an exit code: 0 = success, anything else = failure. The variable $? holds the last one.
ls /nope
echo $? # non-zero
ls /etc
echo $? # 0
You can combine commands based on success:
apt update && apt upgrade -y # run second only if first succeeded
curl -f http://target/ || echo "target unreachable" # run second only if first failed
Cron — schedule scripts to run themselves
crontab -e # edit your scheduled jobs
A line like this runs recon.sh every day at 3am:
0 3 * * * /home/kali/recon.sh >> /home/kali/recon.log 2>&1
Format: minute hour day-of-month month day-of-week command. In Path 15 you'll use cron to keep bug-bounty recon fresh while you sleep.
Best practices worth adopting now
#!/bin/bash
set -euo pipefail
# -e → exit on any error
# -u → treat unset variables as error
# -o pipefail → catch errors mid-pipeline
# Always quote your variables:
mv "$file" "$destination"
# Store constants at the top:
readonly TARGET="scanme.nmap.org"
readonly OUTDIR="/tmp/scan"
# Use meaningful names, not one-letter globals.
Following even half of these makes your scripts vastly more robust — and identifiable as "written by someone who knows Bash."
When Bash isn't enough
Bash is perfect for glue and quick automation. When your script grows past ~100 lines, has complex data structures, or needs proper testing — switch to Python (that's Path 04). The rule of thumb: if it needs a data structure more complex than "list of strings," reach for Python.
🧪 Lab — A recon skeleton
Build this script — you'll extend it in Paths 14 and 10:
#!/bin/bash
# recon.sh — very small recon skeleton
set -euo pipefail
if [ $# -ne 1 ]; then
echo "Usage: $0 <target-domain>"
exit 1
fi
target=$1
outdir="./recon-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$outdir"
echo "[*] Recon on $target"
echo "[*] Output → $outdir"
# Basic DNS
echo "[*] DNS lookups..."
dig "$target" +short > "$outdir/dns-a.txt" 2>&1
dig "$target" MX +short > "$outdir/dns-mx.txt" 2>&1
dig "$target" TXT +short > "$outdir/dns-txt.txt" 2>&1
# HTTP probe
echo "[*] HTTP probes..."
curl -sI "http://$target" > "$outdir/http-headers.txt" 2>&1 || true
curl -sI "https://$target" > "$outdir/https-headers.txt" 2>&1 || true
echo "[+] Done. See $outdir/"
Run it: ./recon.sh example.com. Explore the folder it produces. You've written a real starter recon tool.
What does the shebang line #!/bin/bash do at the top of a script?
Show answer
Tells the system which interpreter to use for the rest of the file — in this case, Bash. Without it, the script may be run by whichever shell you happen to have, which can behave differently.Key terms
- Shebang —
#!line naming the interpreter. - Variable / interpolation —
x=1,$x. - Condition / loop / function — the building blocks of any script.
- Exit code —
0success, non-zero failure. - cron / crontab — schedule scripts to run automatically.
set -euo pipefail— safer-scripts starter kit.
Recap
- A script is a text file of commands starting with a shebang.
- Variables, conditions, loops, functions — Bash has all the essentials for glue work.
- Combine commands with
&&and||for flow control. - Cron schedules scripts to run themselves — huge for automation.
- Beyond ~100 lines or complex data, switch to Python (Path 04).
References
- Bash cheat sheet — Devhints — printable one-pager.
- Advanced Bash-Scripting Guide (TLDP) — encyclopedic reference.
- ShellCheck — paste any Bash script; it points out bugs and bad practice. Life-changing.
- Google's Shell Style Guide — a good template for professional habits.
🎉 You've finished Path 03
You now know what an OS is, how Windows/Linux/macOS differ, why security lives on Linux, how to build a lab with VirtualBox and Kali, the terminal, filesystem navigation, permissions and sudo, and how to write real Bash scripts. This is a huge foundation — most beginners never get this far.
Next up → Shell Scripting Security Pitfalls