What is Bash?
Bash (Bourne Again Shell) is the default command-line shell on most Linux distributions. It reads your commands, runs programs, supports variables, loops, conditionals, and can run scripts stored in files.
Running Commands and Basic Syntax
Simple commands
# Print working directory
pwd
# List files (current directory)
ls
# List with details (long format)
ls -la
# Change directory
cd /home/user
cd .. # parent directory
cd ~ # home directory
cd - # previous directory
# Create directory
mkdir mydir
mkdir -p a/b/c # create parent directories as needed
# Remove empty directory
rmdir mydir
# Remove file (careful: no trash by default)
rm file.txt
rm -r mydir # recursive (directories)
rm -rf mydir # force, no prompts (use with caution)
Redirecting input and output
# Redirect stdout to file (overwrite)
echo "hello" > output.txt
# Append to file
echo "world" >> output.txt
# Redirect stderr
command 2> errors.txt
# Redirect both stdout and stderr
command > output.txt 2>&1
# or in Bash:
command &> output.txt
# Read from file as stdin
command < input.txt
# Pipe: send stdout of one command to stdin of another
ls -l | grep ".txt"
cat file.txt | wc -l
Variables
# Set variable (no spaces around =)
NAME="Alice"
COUNT=42
# Use variable: $NAME or ${NAME}
echo "Hello, $NAME"
echo "Count is ${COUNT}"
# Export so child processes see it
export NAME
# Read-only variable
readonly PI=3.14
# Special variables
echo $? # exit code of last command (0 = success)
echo $0 # script name
echo $1 $2 # first, second argument
echo $# # number of arguments
echo $@ # all arguments as list
echo $$ # current shell PID
Quotes and Escaping
# Double quotes: variables and some escapes are expanded
echo "User is $USER and home is $HOME"
# Single quotes: everything literal, no expansion
echo 'Cost is $100'
# Escaping with backslash
echo "She said \"Hello\""
echo line1\
line2 # line continuation
Conditionals
if / then / else
# Basic if
if [ -f file.txt ]; then
echo "file.txt exists"
fi
# if-else
if [ -d mydir ]; then
echo "mydir is a directory"
else
echo "mydir is not a directory"
fi
# if-elif-else
if [ $COUNT -gt 10 ]; then
echo "Big"
elif [ $COUNT -gt 0 ]; then
echo "Small"
else
echo "Zero or negative"
fi
Test conditions (used inside [ ])
# File tests
[ -f file ] # exists and is regular file
[ -d dir ] # exists and is directory
[ -e path ] # exists
[ -r file ] # readable
[ -w file ] # writable
[ -x file ] # executable
[ -s file ] # size > 0
# String tests
[ -z "$VAR" ] # length is zero
[ -n "$VAR" ] # length is non-zero
[ "$A" = "$B" ] # equal
[ "$A" != "$B" ]# not equal
# Numeric tests (use -eq, -ne, -lt, -le, -gt, -ge)
[ $a -eq $b ]
[ $a -lt 10 ]
case statement
case "$1" in
start)
echo "Starting..."
;;
stop)
echo "Stopping..."
;;
restart)
echo "Restarting..."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
Loops
for loop
# Loop over list
for i in apple banana cherry; do
echo "Fruit: $i"
done
# Loop over range (Bash)
for i in {1..5}; do
echo "Number: $i"
done
# C-style for (Bash)
for ((i=0; i<5; i++)); do
echo "i=$i"
done
# Loop over command output
for f in *.txt; do
echo "Processing: $f"
done
while loop
# While condition is true
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
# Read file line by line
while IFS= read -r line; do
echo "Line: $line"
done < file.txt
# Infinite loop (break with Ctrl+C or break)
while true; do
echo "Running..."
sleep 1
done
until loop
# Run until condition becomes true
count=0
until [ $count -ge 5 ]; do
echo $count
count=$((count + 1))
done
Functions
# Define function
greet() {
echo "Hello, $1"
}
greet "World"
# Function with return (exit code only; use echo for value)
myfunc() {
local x=10
echo $x
}
result=$(myfunc)
echo "Result: $result"
# Return exit code
check_file() {
if [ -f "$1" ]; then
return 0
else
return 1
fi
}
if check_file "config.txt"; then
echo "Config exists"
fi
Arrays (Bash)
# Create array
fruits=(apple banana cherry)
# Access by index (0-based)
echo ${fruits[0]} # apple
echo ${fruits[@]} # all elements
echo ${#fruits[@]} # length
# Loop over array
for f in "${fruits[@]}"; do
echo "$f"
done
# Add element
fruits+=(date)
Command Substitution
# Capture output in variable: $(command) or `command`
DATE=$(date)
echo "Today: $DATE"
FILES=$(ls *.txt)
LINE_COUNT=$(wc -l < file.txt)
# Use in place
echo "There are $(ls | wc -l) items here"
Reading User Input
# Read into variable
read -p "Enter your name: " name
echo "Hello, $name"
# Read multiple values
read -p "Enter first and last: " first last
# Read silently (e.g. password)
read -s -p "Password: " pass
echo
Exit Codes and set Options
# Exit script with code (0 = success)
exit 0
exit 1
# Useful set options (often at top of script)
set -e # exit on first command failure
set -u # error on undefined variable
set -o pipefail # fail pipeline if any command fails
# Example script start
#!/bin/bash
set -euo pipefail
Practical Script Examples
Backup script
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
tar -czf "$BACKUP_DIR/backup-$DATE.tar.gz" /home/user/docs
echo "Backup done: backup-$DATE.tar.gz"
Find and process files
#!/bin/bash
for f in /var/log/*.log; do
if [ -f "$f" ]; then
echo "--- $f ---"
tail -n 5 "$f"
fi
done
Simple menu
#!/bin/bash
echo "1) Option A"
echo "2) Option B"
echo "3) Quit"
read -p "Choice: " choice
case $choice in
1) echo "You chose A" ;;
2) echo "You chose B" ;;
3) exit 0 ;;
*) echo "Invalid" ; exit 1 ;;
esac
Check if service is running
#!/bin/bash
if systemctl is-active --quiet nginx; then
echo "nginx is running"
else
echo "nginx is not running"
fi
Loop with timeout
#!/bin/bash
count=0
max=10
until curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q 200; do
count=$((count + 1))
if [ $count -ge $max ]; then
echo "Timeout waiting for server"
exit 1
fi
sleep 2
done
echo "Server is up"
Log rotation simulation
#!/bin/bash
LOG_DIR="/var/log/myapp"
MAX_FILES=5
cd "$LOG_DIR" || exit 1
ls -t *.log 2>/dev/null | tail -n +$((MAX_FILES + 1)) | xargs -r rm --
echo "Old logs cleaned"
Parse CSV (simple)
#!/bin/bash
while IFS=',' read -r col1 col2 col3; do
echo "Column1: $col1, Column2: $col2, Column3: $col3"
done < data.csv
Safe script with usage
#!/bin/bash
set -euo pipefail
usage() {
echo "Usage: $0 <input_file> <output_file>"
exit 1
}
[ $# -eq 2 ] || usage
INPUT="$1"
OUTPUT="$2"
[ -f "$INPUT" ] || { echo "Input file not found: $INPUT"; exit 1; }
# ... do work ...
echo "Done."
Running Scripts
# Make executable once
chmod +x myscript.sh
# Run (from current directory)
./myscript.sh
# Run with bash explicitly (no execute bit needed)
bash myscript.sh
# Pass arguments
./myscript.sh arg1 arg2
Next Steps
- Use these patterns in daily tasks and small automations.
- Explore Ubuntu network tools from the shell: 03-ubuntu-network-tools.md.