Mastering Bash: A Complete Guide



This content originally appeared on DEV Community and was authored by Maksym

Lately, I’ve started working on new projects that involve a lot of sysadmin responsibilities, so I need to level up my Bash skills to make my life a little easier.
Aliases already help me a lot — I’ve just figured out a system in my head, a kind of naming convention, for how I should call some daily commands that I might run hundreds of times per day.
So join me on this journey! We’ll cover not just simple aliases, but the real power of Bash scripts — a tool that can make your workflow faster, smarter, and way more fun. Enjoy! 😎

Bash (Bourne Again Shell) is the default shell for most Linux distributions and macOS systems. Mastering Bash will dramatically improve your productivity as a developer, system administrator, or power user. This comprehensive guide will take you from basic commands to advanced scripting techniques.

Table of Contents

  1. Getting Started with Bash
  2. Essential Commands
  3. File System Navigation
  4. Text Processing
  5. Variables and Environment
  6. Control Structures
  7. Functions
  8. I/O and Redirection
  9. Process Management
  10. Advanced Features
  11. Best Practices
  12. Common Pitfalls
  13. Conclusion

Getting Started with Bash

What is Bash?

Bash is both a command-line interface and a scripting language. It provides a powerful way to interact with your operating system, automate tasks, and create complex workflows.

Basic Syntax

Every Bash command follows this basic structure:

command [options] [arguments]

For example:

ls -la /home/user

Your First Script

Create a file with a .sh extension:

#!/bin/bash
echo "Hello, World!"

Make it executable and run it:

chmod +x hello.sh
./hello.sh

Essential Commands

File Operations

  • ls – List directory contents
  • cp – Copy files and directories
  • mv – Move/rename files and directories
  • rm – Remove files and directories
  • mkdir – Create directories
  • rmdir – Remove empty directories
  • find – Search for files and directories

Text Operations

  • cat – Display file contents
  • less/more – View file contents page by page
  • head – Display first lines of a file
  • tail – Display last lines of a file
  • grep – Search text patterns
  • sed – Stream editor for text manipulation
  • awk – Text processing tool

System Information

  • ps – Show running processes
  • top/htop – Display system processes
  • df – Show disk space usage
  • du – Show directory space usage
  • free – Show memory usage
  • uname – System information

File System Navigation

Basic Navigation

# Change directory
cd /path/to/directory
cd ~  # Go to home directory
cd -  # Go to previous directory

# Print working directory
pwd

# List with details
ls -la

# Show hidden files
ls -a

Path Management

# Absolute path
/home/user/documents/file.txt

# Relative path
../documents/file.txt
./current-directory/file.txt

# Using wildcards
ls *.txt        # All .txt files
ls file?.txt    # file1.txt, fileA.txt, etc.
ls file[1-3].txt # file1.txt, file2.txt, file3.txt

Text Processing

grep – Pattern Matching

# Basic search
grep "pattern" file.txt

# Case-insensitive search
grep -i "pattern" file.txt

# Recursive search
grep -r "pattern" /directory

# Show line numbers
grep -n "pattern" file.txt

# Invert match (show non-matching lines)
grep -v "pattern" file.txt

sed – Stream Editor

# Replace first occurrence
sed 's/old/new/' file.txt

# Replace all occurrences
sed 's/old/new/g' file.txt

# Replace in-place
sed -i 's/old/new/g' file.txt

# Delete lines containing pattern
sed '/pattern/d' file.txt

# Print specific lines
sed -n '1,5p' file.txt  # Print lines 1-5

awk – Text Processing

# Print specific columns
awk '{print $1, $3}' file.txt

# Print lines with condition
awk '$3 > 100' file.txt

# Sum a column
awk '{sum += $1} END {print sum}' file.txt

# Use custom delimiter
awk -F',' '{print $2}' file.csv

Variables and Environment

Variable Declaration and Usage

# Declare variables (no spaces around =)
name="John"
age=25

# Use variables
echo "Hello, $name"
echo "You are ${age} years old"

# Command substitution
current_date=$(date)
file_count=`ls | wc -l`

# Read user input
read -p "Enter your name: " username
echo "Hello, $username"

Environment Variables

# Common environment variables
echo $HOME      # Home directory
echo $PATH      # Executable paths
echo $USER      # Current user
echo $PWD       # Current directory

# Export variables to subshells
export MY_VAR="value"

# Set PATH
export PATH=$PATH:/new/directory

Special Variables

$0  # Script name
$1, $2, $3  # Script arguments
$#  # Number of arguments
$@  # All arguments as separate strings
$*  # All arguments as single string
$$  # Process ID
$?  # Exit status of last command

Control Structures

Conditional Statements

# if-else statement
if [ condition ]; then
    # commands
elif [ another_condition ]; then
    # commands
else
    # commands
fi

# Examples
if [ $age -gt 18 ]; then
    echo "Adult"
else
    echo "Minor"
fi

# String comparisons
if [ "$string1" = "$string2" ]; then
    echo "Strings match"
fi

# File tests
if [ -f "/path/to/file" ]; then
    echo "File exists"
fi

if [ -d "/path/to/directory" ]; then
    echo "Directory exists"
fi

Loops

# for loop
for i in {1..10}; do
    echo $i
done

for file in *.txt; do
    echo "Processing $file"
done

# while loop
counter=1
while [ $counter -le 5 ]; do
    echo $counter
    ((counter++))
done

# until loop
until [ $counter -gt 10 ]; do
    echo $counter
    ((counter++))
done

Case Statements

case $variable in
    pattern1)
        commands
        ;;
    pattern2|pattern3)
        commands
        ;;
    *)
        default commands
        ;;
esac

# Example
case $1 in
    start)
        echo "Starting service..."
        ;;
    stop)
        echo "Stopping service..."
        ;;
    restart)
        echo "Restarting service..."
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        ;;
esac

Functions

Function Definition

# Method 1
function_name() {
    # commands
    return 0  # optional return value
}

# Method 2
function function_name {
    # commands
}

# Example
greet() {
    local name=$1
    echo "Hello, $name!"
}

# Call function
greet "Alice"

Function Parameters and Local Variables

calculate_sum() {
    local num1=$1
    local num2=$2
    local sum=$((num1 + num2))
    echo $sum
}

result=$(calculate_sum 5 3)
echo "Sum: $result"

I\O and Redirection

Standard Streams

  • stdin (0) – Standard input
  • stdout (1) – Standard output
  • stderr (2) – Standard error

Redirection

# Redirect stdout to file
command > file.txt

# Append stdout to file
command >> file.txt

# Redirect stderr
command 2> error.log

# Redirect both stdout and stderr
command > output.txt 2>&1
command &> output.txt  # Bash 4+

# Redirect stdin
command < input.txt

# Here documents
cat << EOF
This is a here document
Multiple lines of text
EOF

Pipes

# Basic pipe
command1 | command2

# Chain multiple commands
ps aux | grep python | grep -v grep

# Tee - write to file and stdout
command | tee output.txt

# Process substitution
diff <(ls dir1) <(ls dir2)

Process Management

Background and Foreground Processes

# Run in background
command &

# List jobs
jobs

# Bring to foreground
fg %1

# Send to background
bg %1

# Disown process
disown %1

Process Control

# Kill process by PID
kill 1234
kill -9 1234  # Force kill

# Kill by name
killall process_name
pkill process_name

# Process information
ps aux | grep process_name
pgrep process_name

Command Execution Control

# Run commands sequentially
command1; command2; command3

# Run if previous succeeds
command1 && command2

# Run if previous fails
command1 || command2

# Group commands
(command1; command2) | command3
{ command1; command2; } | command3

Advanced Features

Arrays

# Declare array
fruits=("apple" "banana" "orange")

# Access elements
echo ${fruits[0]}      # First element
echo ${fruits[@]}      # All elements
echo ${fruits[*]}      # All elements (different quoting behavior)
echo ${#fruits[@]}     # Array length

# Add elements
fruits+=("grape")

# Loop through array
for fruit in "${fruits[@]}"; do
    echo $fruit
done

String Manipulation

string="Hello, World!"

# String length
echo ${#string}

# Substring
echo ${string:0:5}    # "Hello"
echo ${string:7}      # "World!"

# Replace
echo ${string/Hello/Hi}           # Replace first occurrence
echo ${string//o/0}               # Replace all occurrences

# Remove from beginning/end
filename="document.txt"
echo ${filename%.txt}             # Remove .txt extension
echo ${filename#*/}               # Remove everything up to first /

Regular Expressions

# Pattern matching with [[ ]]
if [[ $string =~ ^[0-9]+$ ]]; then
    echo "String contains only digits"
fi

# Extended globbing
shopt -s extglob
ls *.@(jpg|png|gif)   # Files with jpg, png, or gif extensions

Command Line Shortcuts

# History expansion
!!           # Last command
!n           # Command number n
!string      # Last command starting with string
^old^new     # Replace old with new in last command

# Tab completion
# Press Tab to complete commands, filenames, variables

# Brace expansion
echo {1..10}           # 1 2 3 4 5 6 7 8 9 10
echo {a..z}            # a b c d ... z
mkdir dir{1,2,3}       # Create dir1, dir2, dir3

Best Practices

Script Structure

#!/bin/bash

# Script: backup.sh
# Description: Backup important files
# Author: Your Name
# Date: 2024-01-01

set -euo pipefail  # Exit on error, undefined variables, pipe failures

# Global variables
readonly BACKUP_DIR="/backup"
readonly LOG_FILE="/var/log/backup.log"

# Functions
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

main() {
    log "Starting backup process"
    # Main logic here
    log "Backup completed successfully"
}

# Check if running as root
if [[ $EUID -eq 0 ]]; then
    echo "Don't run this script as root"
    exit 1
fi

# Call main function
main "$@"

Error Handling

# Check command success
if ! command -v git &> /dev/null; then
    echo "Git is not installed"
    exit 1
fi

# Trap for cleanup
cleanup() {
    echo "Cleaning up temporary files"
    rm -f /tmp/tempfile*
}
trap cleanup EXIT

# Validate arguments
if [[ $# -lt 2 ]]; then
    echo "Usage: $0 <source> <destination>"
    exit 1
fi

Security Considerations

# Quote variables to prevent word splitting
cp "$file" "$destination"

# Use arrays for commands with spaces
command=("rsync" "-av" "--delete")
"${command[@]}" "$source/" "$destination/"

# Validate input
read -p "Enter filename: " filename
if [[ ! "$filename" =~ ^[a-zA-Z0-9._-]+$ ]]; then
    echo "Invalid filename"
    exit 1
fi

Common Pitfalls

Quoting Issues

# Wrong - word splitting occurs
for file in $(ls *.txt); do
    echo $file
done

# Right - proper quoting
while IFS= read -r -d '' file; do
    echo "$file"
done < <(find . -name "*.txt" -print0)

Variable Scope

# Global variable modified in subshell
counter=0
cat file.txt | while read line; do
    ((counter++))
done
echo $counter  # Still 0!

# Solution: use process substitution
counter=0
while read line; do
    ((counter++))
done < <(cat file.txt)
echo $counter  # Correct value

File Testing

# Wrong - doesn't handle spaces in filenames
if [ -f $file ]; then

# Right - proper quoting
if [[ -f "$file" ]]; then

Conclusion

Mastering Bash is a journey that pays dividends in productivity and system understanding. Start with the basics and gradually incorporate advanced features into your workflow. Practice regularly, read existing scripts, and don’t hesitate to experiment in a safe environment.

Remember these key principles:

  • Always quote your variables
  • Use set -euo pipefail for robust scripts
  • Handle errors gracefully
  • Write readable, well-commented code
  • Test your scripts thoroughly

With these skills and practices, you’ll be well on your way to becoming a Bash expert. The command line will transform from a intimidating interface into a powerful ally in your daily computing tasks.

And as always please feel free to criticize this article and share your thoughts! Cheers


This content originally appeared on DEV Community and was authored by Maksym