🐚 Bash Introduction
🎯 Complete Definition
Bash (Bourne Again SHell) is a Unix shell and command-line interpreter created by Brian Fox for the GNU Project as a free software replacement for the Bourne shell. It's the default shell on most Linux distributions and macOS, providing a powerful interface for interacting with the operating system, automating tasks, and writing scripts.
🔬 Core Characteristics
- Command Interpretation: Executes user commands and system programs
- Scripting Language: Complete programming language for automation
- Job Control: Manage multiple processes (background, foreground, suspend)
- I/O Redirection: Control input/output streams with operators
- Pipelines: Chain commands together for complex operations
- Wildcards/Globbing: Pattern matching for filenames
- Command History: Recall and reuse previous commands
- Command Aliases: Create shortcuts for frequently used commands
- Environment Variables: Configure shell behavior and store data
📊 Industry Usage
Bash is ubiquitous in system administration, DevOps, cloud computing, and software development. Used for server management, deployment scripts, build automation, data processing pipelines, and development environment configuration. Essential for Linux/Unix administrators, developers, data scientists, and DevOps engineers.
#!/bin/bash
# Bash Introduction - CodeOrbitPro
echo "🐚 Welcome to Bash Pro Track!"
# Basic commands
echo "Current directory: $(pwd)"
echo "Current user: $(whoami)"
echo "System info: $(uname -a)"
# Variables
name="Bash"
version="5.1"
echo "Shell: $name, Version: $version"
# Command substitution
files=$(ls -la | wc -l)
echo "Files in current directory: $files"
# Simple calculation
echo "5 + 3 = $((5 + 3))"
# Conditionals
if [ -f "$0" ]; then
echo "This script exists!"
fi
# Loop
echo "Counting to 5:"
for i in {1..5}; do
echo -n "$i "
done
echo ""
# Exit status
echo "Exit status of last command: $?"
📝 Basics & Syntax
🎯 Complete Definition
Bash syntax follows a specific structure with commands, options, arguments, and operators. Understanding the basic syntax is essential for writing effective shell scripts and using the command line efficiently.
📋 Key Elements
- Shebang: #!/bin/bash - First line of script specifying interpreter
- Commands: Built-ins (echo, cd) or external programs (ls, grep)
- Options: Modify command behavior with - or -- (ls -la, grep -i)
- Arguments: Inputs to commands (ls /home, echo "Hello")
- Comments: # for single-line comments
- Statement Termination: Newline or ; for multiple commands on one line
- Command Separators: ; (sequential), && (AND), || (OR)
- Line Continuation: \ at end of line for multi-line commands
- Quoting: Single quotes (literal), double quotes (interpreted), backticks (command)
🔧 Special Characters
- $ - Variable expansion, command substitution
- # - Comment, number sign
- * - Wildcard, multiplication
- ? - Single character wildcard
- [] - Character class, test command
- {} - Brace expansion
- () - Subshell, command grouping
- | - Pipe
- > - Redirect output
- < - Redirect input
- & - Background process
#!/bin/bash
# Bash Basics & Syntax
# Shebang line tells the system to use bash interpreter
# Comments start with #
# This is a comment and won't be executed
# Basic command
echo "Hello, World!"
# Command with options
ls -la
# Command with arguments
echo "Current user: $(whoami)"
# Multiple commands on one line (using ;)
echo "First"; echo "Second"; echo "Third"
# Conditional execution (&& = AND, || = OR)
true && echo "This runs" # Runs because true succeeds
false || echo "This runs" # Runs because false fails
# Command grouping
{ echo "Group"; echo "Commands"; }
# Subshell
(cd /tmp; pwd) # Changes to /tmp in subshell only
pwd # Still in original directory
# Line continuation (using \)
echo "This is a very long line that \
continues on the next line"
# Quoting examples
name="World"
echo 'Single quotes: $name' # Output: $name (literal)
echo "Double quotes: $name" # Output: World (expanded)
# Command substitution (old style with backticks)
files=`ls`
echo "Files: $files"
# Command substitution (new style with $())
files=$(ls)
echo "Files: $files"
# Exit status
ls /tmp
echo "Exit status: $?" # 0 = success, non-zero = failure
🔤 Variables
🎯 Complete Definition
Bash variables are named storage locations that hold data. They are untyped (everything is a string) and can store text, numbers, filenames, or command output. Variables can be local to a script, exported to child processes, or special system-defined variables.
📋 Variable Types
- User Variables: Created by user (name="John")
- Environment Variables: Exported to child processes (PATH, HOME, USER)
- Special/Positional Parameters: $0, $1, $2... (script arguments)
- Special Variables: $?, $$, $!, $#, $@, $*
- Readonly Variables: Cannot be changed after declaration
- Array Variables: Store multiple values (indexed or associative)
🔧 Variable Operations
- Assignment: name=value (no spaces around =)
- Access: $name or ${name}
- Unset: unset name
- Export: export name (makes available to child processes)
- Readonly: readonly name (prevents changes)
- Default Values: ${var:-default}, ${var:=default}, ${var:?error}
- String Operations: ${#var} (length), ${var:offset:length} (substring)
- Pattern Removal: ${var#pattern}, ${var##pattern}, ${var%pattern}, ${var%%pattern}
- Search/Replace: ${var/pattern/replacement}
#!/bin/bash
# Variables in Bash
# ===== USER VARIABLES =====
# Assignment (no spaces around =)
name="John Doe"
age=30
city="New York"
# Access variables
echo "Name: $name"
echo "Age: $age"
echo "City: ${city}" # Curly braces optional, but good practice
# ===== ENVIRONMENT VARIABLES =====
echo "Home directory: $HOME"
echo "Current user: $USER"
echo "Path: $PATH"
echo "Shell: $SHELL"
echo "Current directory: $PWD"
# Export variables to child processes
export MY_VAR="Hello from parent"
bash -c 'echo "Child process: $MY_VAR"'
# ===== SPECIAL VARIABLES =====
echo "Script name: $0"
echo "Number of arguments: $#"
echo "All arguments as string: $*"
echo "All arguments as array: $@"
echo "Process ID of this script: $$"
echo "Exit status of last command: $?"
# ===== VARIABLE EXPANSION TRICKS =====
# Default values
unset myvar
echo "Default: ${myvar:-default}" # Prints "default", myvar unchanged
echo "Assign default: ${myvar:=default}" # Sets myvar to "default"
echo "Now myvar = $myvar"
# Error if unset
# echo "${myvar:?Error: myvar not set}" # Prints error and exits
# ===== STRING OPERATIONS =====
text="Hello World Bash Scripting"
echo "Length: ${#text}"
echo "Substring (0-5): ${text:0:5}"
echo "Substring (6-5): ${text:6:5}"
echo "Substring from 6: ${text:6}"
# ===== PATTERN REMOVAL =====
filename="archive.tar.gz"
echo "Remove shortest from front: ${filename#*.}" # tar.gz
echo "Remove longest from front: ${filename##*.}" # gz
echo "Remove shortest from back: ${filename%.*}" # archive.tar
echo "Remove longest from back: ${filename%%.*}" # archive
# ===== SEARCH AND REPLACE =====
message="The quick brown fox jumps over the lazy dog"
echo "Replace first 'the' with 'a': ${message/the/a}"
echo "Replace all 'o' with '0': ${message//o/0}"
echo "Replace prefix: ${message/#The/A}"
echo "Replace suffix: ${message/%dog/cat}"
# ===== READONLY VARIABLES =====
readonly PI=3.14159
# PI=3.14 # This would cause an error
# ===== UNSET VARIABLES =====
temp_var="temporary"
unset temp_var
echo "temp_var after unset: $temp_var" # Empty
🧮 Operators
🎯 Complete Definition
Bash operators perform operations on variables and values. They include arithmetic operators, comparison operators, logical operators, file test operators, and string test operators. Understanding operators is crucial for creating conditional logic in scripts.
📊 Arithmetic Operators
- + - Addition
- - - Subtraction
- * - Multiplication
- / - Division (integer division)
- % - Modulus (remainder)
- ** - Exponentiation
- += -= *= /= - Assignment operators
- ++ -- - Increment/Decrement
🔍 Comparison Operators
- Numeric: -eq, -ne, -lt, -le, -gt, -ge
- String: =, ==, !=, <, > (in [[ ]])
- String tests: -z (empty), -n (non-empty)
🔗 Logical Operators
- && - Logical AND
- || - Logical OR
- ! - Logical NOT
📁 File Test Operators
- -e - File exists
- -f - Regular file
- -d - Directory
- -r - Readable
- -w - Writable
- -x - Executable
- -s - Size > 0
- -L - Symbolic link
- -nt - Newer than
- -ot - Older than
#!/bin/bash
# Operators in Bash
# ===== ARITHMETIC OPERATORS =====
# Using $((...)) for arithmetic
a=10
b=3
echo "a = $a, b = $b"
echo "Addition: a + b = $((a + b))"
echo "Subtraction: a - b = $((a - b))"
echo "Multiplication: a * b = $((a * b))"
echo "Division: a / b = $((a / b))" # Integer division
echo "Modulus: a % b = $((a % b))"
echo "Exponentiation: a ** b = $((a ** b))"
# Increment/Decrement
c=5
echo "c = $c"
echo "c++ = $((c++))" # Post-increment
echo "Now c = $c"
echo "++c = $((++c))" # Pre-increment
echo "Now c = $c"
# Assignment operators
d=10
echo "d = $d"
((d += 5))
echo "d += 5 => $d"
((d *= 2))
echo "d *= 2 => $d"
# Using let for arithmetic
let "e = 5 + 3"
echo "let e = 5 + 3 => $e"
let e++
echo "let e++ => $e"
# Using expr (older method)
f=$(expr 10 + 5)
echo "expr 10 + 5 => $f"
# ===== NUMERIC COMPARISONS =====
# Use with test command [ ] or [[ ]]
x=10
y=20
if [ $x -eq $y ]; then echo "$x equals $y"; fi
if [ $x -ne $y ]; then echo "$x not equals $y"; fi
if [ $x -lt $y ]; then echo "$x less than $y"; fi
if [ $x -le $y ]; then echo "$x less or equal $y"; fi
if [ $x -gt $y ]; then echo "$x greater than $y"; fi
if [ $x -ge $y ]; then echo "$x greater or equal $y"; fi
# Using [[ ]] (bash-specific, more features)
if [[ $x -lt $y ]]; then
echo "[[ $x -lt $y ]] is true"
fi
# ===== STRING COMPARISONS =====
str1="hello"
str2="world"
str3="hello"
if [ "$str1" = "$str3" ]; then
echo "Strings are equal (with =)"
fi
if [[ "$str1" == "$str3" ]]; then
echo "Strings are equal (with == in [[ ]])"
fi
if [ "$str1" != "$str2" ]; then
echo "Strings are different"
fi
# String comparison (alphabetical)
if [[ "$str1" < "$str2" ]]; then
echo "'$str1' comes before '$str2'"
fi
# String empty checks
empty=""
if [ -z "$empty" ]; then
echo "String is empty (-z)"
fi
not_empty="text"
if [ -n "$not_empty" ]; then
echo "String is not empty (-n)"
fi
# ===== LOGICAL OPERATORS =====
# && (AND), || (OR), ! (NOT)
a=5
b=10
c=15
if [ $a -lt $b ] && [ $b -lt $c ]; then
echo "Both conditions true (&&)"
fi
if [ $a -gt $b ] || [ $b -lt $c ]; then
echo "At least one condition true (||)"
fi
if ! [ $a -gt $b ]; then
echo "NOT condition (!)"
fi
# Using [[ ]] with && and ||
if [[ $a -lt $b && $b -lt $c ]]; then
echo "Using && inside [[ ]]"
fi
if [[ $a -gt $b || $b -lt $c ]]; then
echo "Using || inside [[ ]]"
fi
# ===== FILE TEST OPERATORS =====
file="/etc/passwd"
dir="/tmp"
link="/usr/bin/bash"
if [ -e "$file" ]; then echo "File exists (-e)"; fi
if [ -f "$file" ]; then echo "Regular file (-f)"; fi
if [ -d "$dir" ]; then echo "Directory (-d)"; fi
if [ -r "$file" ]; then echo "File is readable (-r)"; fi
if [ -w "$file" ]; then echo "File is writable (-w)"; fi
if [ -x "$dir" ]; then echo "Directory is executable/searchable (-x)"; fi
if [ -s "$file" ]; then echo "File size > 0 (-s)"; fi
if [ -L "$link" ]; then echo "Symbolic link (-L)"; fi
# File comparisons
if [ "$file" -nt "$dir" ]; then
echo "File is newer than directory"
fi
# ===== COMBINING TESTS =====
# Complex conditions
if [ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ]; then
echo "File exists, is readable, and has content"
fi
if [[ -f "$file" && -r "$file" && -s "$file" ]]; then
echo "Same check with [[ ]]"
fi
# ===== TERNARY-LIKE OPERATORS =====
# Using && and || for simple conditionals
[[ $x -gt 5 ]] && echo "x > 5" || echo "x <= 5"
# Using if-then-else (more readable)
if [ $x -gt 5 ]; then
echo "x > 5"
else
echo "x <= 5"
fi
📋 Strings
🎯 Complete Definition
Strings in Bash are sequences of characters. Bash provides extensive string manipulation capabilities including concatenation, slicing, pattern matching, and various transformations. Understanding string handling is essential for text processing in shell scripts.
📋 String Operations
- Concatenation: Just place strings/variables together: "Hello $name"
- Length: ${#string}
- Substring: ${string:position:length}
- Pattern removal: ${string#pattern}, ${string##pattern}, ${string%pattern}, ${string%%pattern}
- Search & replace: ${string/pattern/replacement}, ${string//pattern/replacement}
- Case conversion: ${string^}, ${string^^}, ${string,}, ${string,,}
- Trim whitespace: Using pattern removal or external tools
- Split strings: Using IFS or read -a
- Here documents: Multi-line strings with <
- Here strings: <<< for feeding strings to commands
🔧 Quote Types
- Single quotes (''): Preserve literal value of all characters
- Double quotes (""): Allow variable expansion and command substitution
- Backticks (``): Command substitution (old style)
- $'' : ANSI-C quoting (supports escape sequences)
- $"" : Localization quoting
#!/bin/bash
# String Manipulation in Bash
# ===== STRING CONCATENATION =====
first="Hello"
second="World"
space=" "
result="$first$space$second"
echo "Concatenated: $result"
# Direct concatenation
name="John"
greeting="Hello, $name!"
echo "$greeting"
# ===== STRING LENGTH =====
text="Hello, Bash!"
echo "Text: $text"
echo "Length: ${#text}"
# ===== SUBSTRING EXTRACTION =====
text="The quick brown fox"
echo "Original: $text"
echo "First 5 chars: ${text:0:5}"
echo "Chars 4-8: ${text:4:5}"
echo "From position 10: ${text:10}"
echo "Last 3 chars: ${text: -3}" # Space needed before -3
# ===== PATTERN REMOVAL =====
url="https://www.example.com/page.html"
file="archive.tar.gz"
path="/home/user/docs/file.txt"
echo "URL: $url"
echo "Remove http://: ${url#http://}"
echo "Remove up to first dot: ${url#*.}"
echo "Remove up to last dot: ${url##*.}"
echo "Remove domain: ${url##*/}"
echo "File: $file"
echo "Remove shortest suffix: ${file%.*}" # archive.tar
echo "Remove longest suffix: ${file%%.*}" # archive
echo "Remove shortest prefix: ${file#*.}" # tar.gz
echo "Remove longest prefix: ${file##*.}" # gz
echo "Path: $path"
echo "Directory: ${path%/*}" # /home/user/docs
echo "Filename: ${path##*/}" # file.txt
# ===== SEARCH AND REPLACE =====
sentence="The cat sat on the mat"
echo "Original: $sentence"
echo "Replace first 'cat' with 'dog': ${sentence/cat/dog}"
echo "Replace first 'at' with 'AT': ${sentence/at/AT}"
echo "Replace all 'at' with 'AT': ${sentence//at/AT}"
echo "Replace prefix 'The' with 'A': ${sentence/#The/A}"
echo "Replace suffix 'mat' with 'rug': ${sentence/%mat/rug}"
# ===== CASE CONVERSION =====
text="hello WORLD"
echo "Original: $text"
echo "First char uppercase: ${text^}"
echo "All uppercase: ${text^^}"
echo "First char lowercase: ${text,}"
echo "All lowercase: ${text,,}"
echo "Title case (each word):"
for word in $text; do
echo -n "${word^} "
done
echo ""
# ===== STRING SPLITTING =====
# Using IFS
data="apple,banana,orange,grape"
IFS=',' read -ra fruits <<< "$data"
echo "Split fruits:"
for fruit in "${fruits[@]}"; do
echo " - $fruit"
done
# Using read -a for word splitting
sentence="This is a sample sentence"
read -ra words <<< "$sentence"
echo "Words: ${words[0]}, ${words[1]}, ${words[2]}"
# ===== HERE DOCUMENTS =====
# Multi-line string with <
📊 Arrays
🎯 Complete Definition
Bash arrays are data structures that store multiple values under a single variable name. Bash supports both indexed arrays (with numeric indices) and associative arrays (with string keys). Arrays are powerful for managing collections of data in shell scripts.
📋 Array Types
- Indexed Arrays: Numeric indices starting at 0 (default)
- Associative Arrays: String keys (declare -A)
🔧 Array Operations
- Declaration: array=(), declare -a array, declare -A assoc
- Assignment: array[0]="value", array+=(element), array=(val1 val2)
- Access: ${array[index]}
- All elements: ${array[@]}, ${array[*]}
- Indices: ${!array[@]}
- Length: ${#array[@]}
- Slicing: ${array[@]:start:count}
- Delete element: unset array[index]
- Delete array: unset array
#!/bin/bash
# Arrays in Bash
# ===== INDEXED ARRAYS =====
# Declaration methods
fruits=("apple" "banana" "orange") # Explicit
colors[0]="red" # Individual
colors[1]="green"
colors[2]="blue"
declare -a numbers # Declare empty array
numbers+=(1 2 3 4 5) # Append
echo "Fruits: ${fruits[@]}"
echo "Colors: ${colors[@]}"
echo "Numbers: ${numbers[@]}"
# ===== ACCESSING ARRAY ELEMENTS =====
echo "First fruit: ${fruits[0]}"
echo "Second fruit: ${fruits[1]}"
echo "Third fruit: ${fruits[2]}"
# ===== ARRAY LENGTH =====
echo "Number of fruits: ${#fruits[@]}"
echo "Length of first fruit: ${#fruits[0]}"
# ===== ALL ELEMENTS =====
# Using @ (preserves word splitting)
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# ===== ARRAY INDICES =====
echo "Indices of fruits: ${!fruits[@]}"
# ===== APPENDING ELEMENTS =====
fruits+=("grape" "kiwi")
echo "After append: ${fruits[@]}"
# ===== REMOVING ELEMENTS =====
unset fruits[1] # Remove element at index 1
echo "After removing index 1: ${fruits[@]}"
# ===== SLICING ARRAYS =====
numbers=(1 2 3 4 5 6 7 8 9 10)
echo "First 3: ${numbers[@]:0:3}"
echo "From index 4: ${numbers[@]:4}"
echo "Last 3: ${numbers[@]: -3}"
# ===== ASSOCIATIVE ARRAYS =====
# Must declare with -A
declare -A user
# Assign values
user["name"]="John Doe"
user["age"]=30
user["city"]="New York"
user["email"]="john@example.com"
# Access values
echo "Name: ${user["name"]}"
echo "Age: ${user["age"]}"
echo "City: ${user["city"]}"
# List all keys and values
echo "All keys: ${!user[@]}"
echo "All values: ${user[@]}"
# Iterate over associative array
for key in "${!user[@]}"; do
echo "$key: ${user[$key]}"
done
# ===== ARRAY FROM COMMAND OUTPUT =====
# Split output into array
mapfile -t files < <(ls -la)
echo "Files in current directory: ${#files[@]}"
echo "First 3 files:"
for ((i=0; i<3 && i<${#files[@]}; i++)); do
echo " ${files[$i]}"
done
# ===== PRACTICAL EXAMPLES =====
# 1. Parse CSV line
csv_line="John,30,New York,Engineer"
IFS=',' read -ra fields <<< "$csv_line"
echo "CSV fields:"
for i in "${!fields[@]}"; do
echo " [$i]: ${fields[$i]}"
done
# 2. Unique values
values=(apple banana apple orange banana grape)
declare -A seen
unique=()
for value in "${values[@]}"; do
if [[ -z "${seen[$value]}" ]]; then
seen[$value]=1
unique+=("$value")
fi
done
echo "Unique values: ${unique[@]}"
# 3. Stack implementation
stack=()
push() {
stack+=("$1")
}
pop() {
if [ ${#stack[@]} -gt 0 ]; then
local last_index=$((${#stack[@]} - 1))
local value="${stack[$last_index]}"
unset 'stack[$last_index]'
stack=("${stack[@]}") # Re-index
echo "$value"
fi
}
push "first"
push "second"
push "third"
echo "Stack: ${stack[@]}"
echo "Pop: $(pop)"
echo "Stack now: ${stack[@]}"
🔄 Conditionals
🎯 Complete Definition
Conditionals in Bash allow scripts to make decisions based on conditions. They evaluate commands or test expressions and execute different code paths based on the result. Bash provides several conditional constructs including if, elif, else, case, and the test command.
📋 Conditional Constructs
- if statement: Basic conditional branching
- if-else: Two-way branching
- if-elif-else: Multi-way branching
- case statement: Pattern matching conditional
- && and ||: Short-circuit conditionals
- test command: [ ] or [[ ]] for evaluating conditions
- Conditional expressions: (( )) for arithmetic conditions
🔧 Test Constructs
- [ ] (single brackets): POSIX test command
- [[ ]] (double brackets): Bash extended test (more features)
- (( )) : Arithmetic evaluation
- && and || : Command chaining based on success/failure
#!/bin/bash
# Conditionals in Bash
# ===== BASIC IF STATEMENT =====
if [ -f "/etc/passwd" ]; then
echo "Password file exists"
fi
# ===== IF-ELSE STATEMENT =====
age=18
if [ $age -ge 18 ]; then
echo "You are an adult"
else
echo "You are a minor"
fi
# ===== IF-ELIF-ELSE =====
score=85
if [ $score -ge 90 ]; then
grade="A"
elif [ $score -ge 80 ]; then
grade="B"
elif [ $score -ge 70 ]; then
grade="C"
elif [ $score -ge 60 ]; then
grade="D"
else
grade="F"
fi
echo "Score: $score, Grade: $grade"
# ===== USING DOUBLE BRACKETS [[ ]] =====
# [[ ]] has more features and is safer
name="John Doe"
if [[ $name =~ ^John ]]; then
echo "Name starts with John"
fi
# Pattern matching in [[ ]]
if [[ $name == *"Doe"* ]]; then
echo "Name contains Doe"
fi
# Regular expressions
phone="123-456-7890"
if [[ $phone =~ ^[0-9]{3}-[0-9]{3}-[0-9]{4}$ ]]; then
echo "Valid phone number format"
fi
# ===== ARITHMETIC CONDITIONS WITH (( )) =====
x=10
y=20
if (( x > 5 )); then
echo "x is greater than 5"
fi
if (( x + y > 25 )); then
echo "Sum is greater than 25"
fi
# ===== CASE STATEMENT =====
# Pattern matching for multi-way branching
fruit="apple"
case $fruit in
"apple")
echo "It's an apple"
;;
"banana")
echo "It's a banana"
;;
"orange"|"grapefruit")
echo "It's a citrus fruit"
;;
*)
echo "Unknown fruit: $fruit"
;;
esac
# ===== SHORT-CIRCUIT CONDITIONALS =====
# Using && (AND)
[ -f "/etc/passwd" ] && echo "File exists"
# Using || (OR)
[ -f "/nonexistent" ] || echo "File does not exist"
# Combined
[ -f "/etc/passwd" ] && echo "File exists" || echo "File does not exist"
# ===== FILE TESTS =====
file="/etc/passwd"
dir="/tmp"
if [ -e "$file" ]; then echo "Exists"; fi
if [ -f "$file" ]; then echo "Regular file"; fi
if [ -d "$dir" ]; then echo "Directory"; fi
# ===== STRING TESTS =====
str1="hello"
str2=""
if [ -z "$str2" ]; then echo "String is empty"; fi
if [ -n "$str1" ]; then echo "String is not empty"; fi
if [ "$str1" = "hello" ]; then echo "Strings equal"; fi
# ===== NUMERIC TESTS =====
a=10
b=20
if [ $a -eq 10 ]; then echo "a equals 10"; fi
if [ $a -ne $b ]; then echo "a not equal to b"; fi
if [ $a -lt $b ]; then echo "a less than b"; fi
# ===== COMPOUND CONDITIONS =====
if [[ $a -gt 5 && $b -lt 30 ]]; then
echo "Both conditions true (using &&)"
fi
if [[ $a -gt 20 || $b -lt 30 ]]; then
echo "At least one condition true (using ||)"
fi
# ===== NEGATION =====
if ! [ -f "/nonexistent" ]; then
echo "File does not exist (negation)"
fi
# ===== PRACTICAL EXAMPLES =====
# 1. Check if command exists
if command -v python3 &> /dev/null; then
echo "Python 3 is installed"
else
echo "Python 3 is not installed"
fi
# 2. Check multiple conditions
if [ $# -eq 0 ]; then
echo "No arguments provided"
elif [ $# -eq 1 ]; then
echo "One argument: $1"
else
echo "Multiple arguments"
fi
# 3. Interactive yes/no prompt
read -p "Continue? (y/n): " answer
case $answer in
[Yy]*)
echo "Continuing..."
;;
[Nn]*)
echo "Exiting..."
exit 0
;;
*)
echo "Please answer yes or no"
;;
esac
🔄 Loops
🎯 Complete Definition
Loops in Bash allow repetitive execution of commands. Bash provides several loop constructs: for loops (iterating over lists), while loops (execute while condition true), until loops (execute until condition true), and select loops (interactive menus).
📋 Loop Types
- for loop: Iterate over a list of values
- while loop: Execute while condition is true
- until loop: Execute until condition becomes true
- select loop: Create interactive menus
- break: Exit the loop early
- continue: Skip to next iteration
🔧 Loop Control
- break [n]: Exit from loop (n levels)
- continue [n]: Skip to next iteration (n levels)
- Loop redirection: Redirect output of entire loop
- Pipes with loops: Connect loop output to other commands
#!/bin/bash
# Loops in Bash
# ===== FOR LOOP (TRADITIONAL STYLE) =====
echo "Traditional for loop:"
for i in 1 2 3 4 5; do
echo "Number: $i"
done
# ===== FOR LOOP WITH RANGE =====
echo "For loop with range:"
for i in {1..10}; do
echo -n "$i "
done
echo ""
# Range with step (bash 4+)
echo "For loop with step:"
for i in {1..10..2}; do
echo -n "$i "
done
echo ""
# ===== FOR LOOP WITH C-STYLE SYNTAX =====
echo "C-style for loop:"
for ((i=0; i<10; i++)); do
echo -n "$i "
done
echo ""
# ===== FOR LOOP OVER ARRAY =====
fruits=("apple" "banana" "orange" "grape" "kiwi")
echo "For loop over array:"
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# ===== FOR LOOP OVER FILES =====
echo "For loop over files:"
for file in /etc/*.conf; do
if [ -f "$file" ]; then
echo "Config file: $(basename "$file")"
fi
done | head -5
# ===== WHILE LOOP =====
echo "While loop:"
counter=1
while [ $counter -le 5 ]; do
echo "Counter: $counter"
((counter++))
done
# ===== WHILE LOOP READING FILE =====
echo "While loop reading file:"
while IFS= read -r line; do
echo "Line: $line"
done < /etc/passwd | head -3
# ===== UNTIL LOOP =====
echo "Until loop:"
counter=1
until [ $counter -gt 5 ]; do
echo "Counter: $counter"
((counter++))
done
# ===== SELECT LOOP (MENU) =====
echo "Select loop (choose an option):"
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"; do
case $opt in
"Option 1")
echo "You chose option 1"
;;
"Option 2")
echo "You chose option 2"
;;
"Option 3")
echo "You chose option 3"
;;
"Quit")
echo "Exiting menu"
break
;;
*)
echo "Invalid option"
;;
esac
done
# ===== NESTED LOOPS =====
echo "Nested loops:"
for i in {1..3}; do
for j in {1..3}; do
echo -n "($i,$j) "
done
echo ""
done
# ===== LOOP CONTROL: BREAK =====
echo "Break example:"
for i in {1..10}; do
if [ $i -eq 5 ]; then
echo "Breaking at $i"
break
fi
echo "Number: $i"
done
# ===== LOOP CONTROL: CONTINUE =====
echo "Continue example:"
for i in {1..10}; do
if [ $((i % 2)) -eq 0 ]; then
continue # Skip even numbers
fi
echo "Odd number: $i"
done
# ===== LOOP REDIRECTION =====
echo "Loop output redirection:"
for i in {1..5}; do
echo "Line $i"
done > /tmp/loop_output.txt
echo "Output written to /tmp/loop_output.txt"
# ===== PRACTICAL EXAMPLES =====
# 1. Find and process files
echo "Find and process .sh files:"
for script in *.sh; do
if [ -f "$script" ] && [ -x "$script" ]; then
echo "Executable script: $script"
fi
done | head -5
# 2. Retry loop with timeout
max_attempts=5
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt of $max_attempts"
# Simulate success on attempt 3
if [ $attempt -eq 3 ]; then
echo "Success on attempt $attempt"
break
fi
((attempt++))
sleep 0.5
done
# 3. Progress bar example
echo -n "Progress: "
for i in {1..20}; do
echo -n "▓"
sleep 0.05
done
echo " Done!"
⚙️ Functions
🎯 Complete Definition
Functions in Bash are reusable blocks of code that can be called multiple times. They help organize scripts, reduce code duplication, and improve maintainability. Functions can accept arguments, return values, and have local variables.
📋 Function Components
- Function name: Identifier for the function
- Function body: Commands to execute
- Arguments: Accessible as $1, $2, ... $n
- Return value: Return status (0-255) or output
- Local variables: Variables scoped to function
🔧 Function Features
- Declaration: function name { ... } or name() { ... }
- Argument handling: $@, $*, $#, shift
- Return status: return command (0=success, non-zero=failure)
- Output return: echo/printf to capture output
- Local variables: local keyword
- Variable scope: Global by default, local for function
#!/bin/bash
# Functions in Bash
# ===== BASIC FUNCTION DECLARATION =====
# Method 1: Using function keyword
function greet {
echo "Hello, World!"
}
# Method 2: Using parentheses (more common)
say_hello() {
echo "Hello from say_hello!"
}
# Call functions
greet
say_hello
# ===== FUNCTION WITH ARGUMENTS =====
greet_person() {
local name="$1"
echo "Hello, $name!"
}
greet_person "Alice"
greet_person "Bob"
# ===== FUNCTION WITH MULTIPLE ARGUMENTS =====
add_numbers() {
local num1=$1
local num2=$2
local sum=$((num1 + num2))
echo "Sum of $num1 and $num2 is: $sum"
}
add_numbers 10 20
# ===== FUNCTION WITH RETURN STATUS =====
is_even() {
local num=$1
if [ $((num % 2)) -eq 0 ]; then
return 0 # Success (true)
else
return 1 # Failure (false)
fi
}
is_even 4 && echo "4 is even" || echo "4 is odd"
is_even 5 && echo "5 is even" || echo "5 is odd"
# ===== FUNCTION RETURNING VALUE VIA ECHO =====
get_greeting() {
local name=$1
echo "Hello, $name! Welcome to Bash."
}
greeting=$(get_greeting "Alice")
echo "$greeting"
# ===== FUNCTION WITH DEFAULT ARGUMENTS =====
power() {
local base=${1:-2}
local exponent=${2:-2}
echo $((base ** exponent))
}
echo "2^2 = $(power)"
echo "3^2 = $(power 3)"
echo "4^3 = $(power 4 3)"
# ===== FUNCTION WITH LOCAL VARIABLES =====
calculate() {
local a=$1
local b=$2
local result=$((a * b + 10))
echo "Result: $result"
}
calculate 5 3
# ===== FUNCTION USING ALL ARGUMENTS =====
print_all() {
echo "Number of arguments: $#"
echo "All arguments:"
for arg in "$@"; do
echo " - $arg"
done
}
print_all apple banana orange grape
# ===== FUNCTION WITH SHIFT =====
process_args() {
while [ $# -gt 0 ]; do
echo "Processing: $1"
shift
done
}
process_args first second third fourth
# ===== RECURSIVE FUNCTION =====
factorial() {
local n=$1
if [ $n -le 1 ]; then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
echo "Factorial of 5: $(factorial 5)"
# ===== FUNCTION LIBRARY EXAMPLE =====
# Math functions
math_add() { echo $(( $1 + $2 )); }
math_sub() { echo $(( $1 - $2 )); }
math_mul() { echo $(( $1 * $2 )); }
math_div() { echo $(( $1 / $2 )); }
# String functions
str_length() { echo ${#1}; }
str_upper() { echo "${1^^}"; }
str_lower() { echo "${1,,}"; }
# Use library functions
echo "5 + 3 = $(math_add 5 3)"
echo "Length of 'hello': $(str_length "hello")"
echo "Uppercase: $(str_upper "hello world")"
# ===== PRACTICAL EXAMPLES =====
# 1. Logging function
log() {
local level=$1
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message"
}
log "INFO" "Script started"
log "ERROR" "Something went wrong"
# 2. Configuration file parser
parse_config() {
local config_file=$1
while IFS='=' read -r key value; do
if [[ ! $key =~ ^[[:space:]]*# && -n $key ]]; then
key=$(echo $key | xargs)
value=$(echo $value | xargs)
printf -v "$key" "%s" "$value"
fi
done < "$config_file"
}
# Create test config
cat > /tmp/test.conf <
📂 I/O & Redirection
🎯 Complete Definition
Input/Output redirection in Bash controls where command input comes from and where output goes. Every command has three standard streams: stdin (0), stdout (1), and stderr (2). Redirection allows connecting commands, files, and devices.
📋 Standard Streams
- stdin (0): Standard input (keyboard by default)
- stdout (1): Standard output (terminal by default)
- stderr (2): Standard error (terminal by default)
🔧 Redirection Operators
- > - Redirect stdout to file (overwrite)
- >> - Redirect stdout to file (append)
- < - Use file as stdin
- 2> - Redirect stderr to file
- 2>> - Append stderr to file
- &> - Redirect both stdout and stderr (overwrite)
- &>> - Append both stdout and stderr
- | - Pipe: connect stdout of one command to stdin of another
- tee - Split output: write to file and stdout
- Here documents (<<) - Multi-line input
- Here strings (<<<) - Single-line input
#!/bin/bash
# I/O Redirection in Bash
# ===== OUTPUT REDIRECTION (stdout) =====
# Overwrite file
echo "This goes to file" > /tmp/output.txt
cat /tmp/output.txt
# Append to file
echo "This also goes to file" >> /tmp/output.txt
cat /tmp/output.txt
# ===== ERROR REDIRECTION (stderr) =====
# Redirect errors to file
ls /nonexistent 2> /tmp/error.txt
cat /tmp/error.txt
# ===== REDIRECT BOTH stdout AND stderr =====
# Method 1: &> (bash-specific)
ls /tmp /nonexistent &> /tmp/both.txt
cat /tmp/both.txt
# Method 2: > file 2>&1 (POSIX-compatible)
ls /tmp /nonexistent > /tmp/both2.txt 2>&1
cat /tmp/both2.txt
# ===== DISCARD OUTPUT =====
# Discard stdout
ls /tmp > /dev/null
# Discard stderr
ls /nonexistent 2> /dev/null
# Discard both
ls /tmp /nonexistent &> /dev/null
# ===== PIPES =====
# Connect commands
echo "hello world" | wc -w
# Multiple pipes
cat /etc/passwd | cut -d: -f1 | sort | uniq | head -5
# ===== TEE COMMAND =====
# Write to file and stdout simultaneously
echo "Important data" | tee /tmp/tee_output.txt
cat /tmp/tee_output.txt
# Append with tee -a
echo "More data" | tee -a /tmp/tee_output.txt
# ===== HERE DOCUMENTS =====
# Multi-line input
cat < /tmp/here_doc.txt
This is line 1
This is line 2
This is line 3
EOF
cat /tmp/here_doc.txt
# Here document with variable expansion
name="Alice"
cat < /tmp/loop_output.txt
# Redirect loop input
while read line; do
echo "Read: $line"
done < /etc/passwd | head -3
# ===== PRACTICAL EXAMPLES =====
# 1. Logging script output
{
echo "Script started at $(date)"
echo "User: $USER"
ls -la
echo "Script ended at $(date)"
} > /tmp/script_log.txt
# 2. Error logging with timestamp
{
echo "[$(date)] Starting operation"
ls /nonexistent 2>&1
echo "[$(date)] Operation completed"
} >> /tmp/operation.log 2>&1
# 3. Progress with tee
for i in {1..5}; do
echo "Processing item $i"
sleep 0.1
done | tee /tmp/progress.txt
🔍 Regular Expressions
🎯 Complete Definition
Regular expressions (regex) in Bash are patterns used for matching text. They're used with commands like grep, sed, awk, and in [[ =~ ]] conditional expressions. Bash supports both basic and extended regular expressions.
📋 Regex Types
- Basic Regular Expressions (BRE): Default in grep, sed
- Extended Regular Expressions (ERE): grep -E, egrep, sed -E, awk
🔧 Regex Metacharacters
- . - Any single character
- * - Zero or more of previous character
- + - One or more of previous (ERE)
- ? - Zero or one of previous (ERE)
- ^ - Start of line/string
- $ - End of line/string
- [abc] - Character class (a, b, or c)
- [^abc] - Negated character class
- [a-z] - Range
- {n} - Exactly n times
- {n,} - n or more times
- {n,m} - Between n and m times
- | - Alternation (OR)
- () - Grouping
- \ - Escape special character
#!/bin/bash
# Regular Expressions in Bash
# ===== GREP WITH REGEX =====
echo "=== GREP Examples ==="
# Basic regex
echo "hello world" | grep "hello"
echo "hello world" | grep "^hello" # Starts with hello
echo "hello world" | grep "world$" # Ends with world
# Character classes
echo "cat" | grep "[a-z]at" # Matches cat, bat, rat, etc.
echo "123" | grep -E "[0-9]+" # One or more digits
# Extended regex with grep -E
echo "color colour" | grep -E "colou?r" # Matches both color and colour
# ===== SED WITH REGEX =====
echo "=== SED Examples ==="
# Substitution
echo "Hello World" | sed 's/World/Bash/'
# Global substitution
echo "one two three two" | sed 's/two/2/g'
# Using regex in sed
echo "abc123def456" | sed 's/[0-9]\+/NUM/g'
# Remove HTML tags
echo "
Hello
" | sed 's/<[^>]*>//g'
# ===== AWK WITH REGEX =====
echo "=== AWK Examples ==="
# Pattern matching
echo "apple 10" | awk '/apple/ {print}'
# Field matching
echo "apple:10:red" | awk -F: '$1 ~ /apple/ {print $2}'
# ===== BASH [[ =~ ]] REGEX =====
echo "=== Bash [[ =~ ]] Examples ==="
# Test if string matches pattern
email="user@example.com"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email: $email"
else
echo "Invalid email"
fi
# Capture groups
phone="123-456-7890"
if [[ $phone =~ ^([0-9]{3})-([0-9]{3})-([0-9]{4})$ ]]; then
echo "Area code: ${BASH_REMATCH[1]}"
echo "Exchange: ${BASH_REMATCH[2]}"
echo "Number: ${BASH_REMATCH[3]}"
fi
# ===== COMMON PATTERNS =====
echo "=== Common Patterns ==="
# Email validation
email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
test_emails=(
"user@example.com"
"first.last@domain.co.uk"
"invalid@email"
)
for email in "${test_emails[@]}"; do
if [[ $email =~ $email_pattern ]]; then
echo "✓ $email"
else
echo "✗ $email"
fi
done
# URL validation
url_pattern='^(https?|ftp)://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/[^[:space:]]*)?$'
test_urls=(
"https://example.com"
"http://sub.domain.co.uk/page"
"ftp://ftp.example.com/file.txt"
"invalid-url"
)
for url in "${test_urls[@]}"; do
if [[ $url =~ $url_pattern ]]; then
echo "✓ $url"
else
echo "✗ $url"
fi
done
# IP address validation
ip_pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
test_ips=(
"192.168.1.1"
"10.0.0.255"
"256.1.2.3"
)
for ip in "${test_ips[@]}"; do
if [[ $ip =~ $ip_pattern ]]; then
# Additional check for valid range
valid=true
IFS='.' read -ra octets <<< "$ip"
for octet in "${octets[@]}"; do
if [ $octet -gt 255 ]; then
valid=false
break
fi
done
if $valid; then
echo "✓ $ip (valid)"
else
echo "✗ $ip (invalid range)"
fi
else
echo "✗ $ip (invalid format)"
fi
done
# ===== PRACTICAL EXAMPLES =====
# 1. Extract email from text
text="Contact us at support@example.com or sales@example.com"
emails=$(echo "$text" | grep -o -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}')
echo "Found emails: $emails"
# 2. Validate and format phone numbers
format_phone() {
local phone=$1
if [[ $phone =~ ^([0-9]{3})[-.]?([0-9]{3})[-.]?([0-9]{4})$ ]]; then
echo "(${BASH_REMATCH[1]}) ${BASH_REMATCH[2]}-${BASH_REMATCH[3]}"
else
echo "Invalid phone: $phone"
fi
}
format_phone "123-456-7890"
format_phone "123.456.7890"
format_phone "1234567890"
format_phone "12345"
# 3. Extract all links from HTML (simple version)
html='
Example Page'
links=$(echo "$html" | grep -o -E 'href="([^"]+)"' | sed 's/href="//;s/"//')
echo "Links found:"
echo "$links"
⚡ Process Management
🎯 Complete Definition
Process management in Bash involves controlling and monitoring running programs. Bash provides facilities for running processes in the background, foreground, suspending them, and managing job control. Understanding process management is essential for system administration and scripting.
📋 Process Concepts
- Process ID (PID): Unique identifier for each process
- Parent Process ID (PPID): ID of the parent process
- Job: A process or group of processes running from the shell
- Foreground: Process that has control of the terminal
- Background: Process running without terminal control
- Daemon: Background process not attached to a terminal
🔧 Process Commands
- ps: Display process status
- top/htop: Interactive process viewer
- kill: Send signals to processes
- jobs: List background jobs
- bg: Resume job in background
- fg: Resume job in foreground
- &: Run command in background
- Ctrl+Z: Suspend foreground process
- Ctrl+C: Terminate foreground process
- nohup: Run immune to hangups
- disown: Remove job from job table
- wait: Wait for background processes
#!/bin/bash
# Process Management in Bash
# ===== BACKGROUND PROCESSES =====
echo "Starting background process"
sleep 30 &
echo "Background process started with PID: $!"
# ===== JOB CONTROL =====
echo "=== Job Control ==="
# Start a long-running process
sleep 60 &
sleep 60 &
# List jobs
echo "Current jobs:"
jobs
# Bring job to foreground (uncomment to use)
# fg %1
# Send job to background
# bg %1
# ===== PROCESS INFORMATION =====
echo "=== Process Information ==="
# List processes
echo "Current shell processes:"
ps -f
# List all processes
echo "All processes (first 5 lines):"
ps -ef | head -5
# Process tree
echo "Process tree:"
pstree -h | head -5
# ===== PROCESS IDS =====
echo "=== Process IDs ==="
echo "Current shell PID: $$"
echo "Parent shell PID: $PPID"
echo "Last background PID: $!"
# ===== SIGNALS =====
echo "=== Signals ==="
# Function to handle signals
handle_signal() {
echo "Received signal: $1"
}
# Set up signal handlers
trap 'handle_signal SIGINT' INT
trap 'handle_signal SIGTERM' TERM
trap 'handle_signal SIGUSR1' USR1
# Send signals to self
echo "Sending SIGUSR1 to self"
kill -USR1 $$
# ===== WAITING FOR PROCESSES =====
echo "=== Waiting for Processes ==="
# Start multiple background processes
sleep 2 &
pid1=$!
sleep 3 &
pid2=$!
sleep 1 &
pid3=$!
echo "Waiting for processes to complete..."
wait $pid1 $pid2 $pid3
echo "All processes completed"
# ===== PROCESS SUBSTITUTION =====
echo "=== Process Substitution ==="
# Compare outputs without temporary files
diff <(ls /tmp) <(ls /var/tmp) 2>/dev/null || echo "Directories differ"
# ===== PIPES AND PROCESSES =====
echo "=== Pipes and Processes ==="
# Pipeline creates subshells
echo "Pipeline processes:"
ps -f | head -3
# ===== PROCESS MONITORING =====
echo "=== Process Monitoring ==="
# Check if process is running
check_process() {
local pid=$1
if kill -0 $pid 2>/dev/null; then
echo "Process $pid is running"
return 0
else
echo "Process $pid is not running"
return 1
fi
}
# Start a process and check it
sleep 5 &
pid=$!
check_process $pid
sleep 6
check_process $pid
# ===== PROCESS PRIORITY =====
echo "=== Process Priority ==="
# Start process with lower priority (nice)
nice -n 19 sleep 10 &
echo "Started low priority process with PID: $!"
# Change priority (renice) - requires root
# renice +5 $pid
# ===== PROCESS LIMITS =====
echo "=== Process Limits ==="
echo "Max user processes: $(ulimit -u)"
echo "Open file limit: $(ulimit -n)"
# ===== DAEMON PROCESS =====
echo "=== Daemon Process Example ==="
# Simple daemon function
run_daemon() {
while true; do
echo "Daemon running at $(date)" >> /tmp/daemon.log
sleep 5
done
}
# Start daemon (uncomment to run)
# run_daemon &
# echo "Daemon started with PID: $!"
# ===== PRACTICAL EXAMPLES =====
# 1. Process monitoring script
monitor_process() {
local process_name=$1
local pid=$(pgrep -f "$process_name" | head -1)
if [ -n "$pid" ]; then
echo "Process $process_name (PID: $pid) is running"
echo "CPU usage: $(ps -p $pid -o %cpu | tail -1)%"
echo "Memory usage: $(ps -p $pid -o %mem | tail -1)%"
echo "Runtime: $(ps -p $pid -o etime | tail -1)"
else
echo "Process $process_name not found"
fi
}
# Monitor a process
monitor_process "bash"
# 2. Timeout command
timeout_command() {
local timeout=$1
shift
local cmd=("$@")
"${cmd[@]}" &
local pid=$!
sleep $timeout
if kill -0 $pid 2>/dev/null; then
kill $pid
echo "Command timed out after $timeout seconds"
return 1
fi
wait $pid
return $?
}
# Test timeout
timeout_command 2 sleep 5 || echo "Command failed"
# 3. Parallel process limit
run_parallel() {
local max_procs=$1
shift
local commands=("$@")
local running=0
local pids=()
for cmd in "${commands[@]}"; do
# Wait if we've reached max processes
while [ ${#pids[@]} -ge $max_procs ]; do
for i in "${!pids[@]}"; do
if ! kill -0 ${pids[$i]} 2>/dev/null; then
unset pids[$i]
fi
done
pids=("${pids[@]}")
sleep 0.1
done
# Run command
eval "$cmd" &
pids+=($!)
echo "Started: $cmd (PID: $!)"
done
# Wait for remaining processes
wait
}
# Test parallel execution
run_parallel 3 \
"sleep 2" \
"sleep 3" \
"sleep 1" \
"sleep 4" \
"sleep 2"
# 4. Process tree killer
kill_tree() {
local parent_pid=$1
local signal=${2:-TERM}
# Get child PIDs
local children=$(pgrep -P $parent_pid)
# Kill children first
for child in $children; do
kill_tree $child $signal
done
# Kill parent
kill -$signal $parent_pid 2>/dev/null
echo "Killed process $parent_pid"
}
# Example: kill_tree 1234
# 5. Process resource monitor
resource_monitor() {
local pid=$1
local interval=${2:-1}
echo "Monitoring PID $pid every $interval second(s)"
echo "Time CPU% MEM% VSZ RSS"
while kill -0 $pid 2>/dev/null; do
ps -p $pid -o pcpu=,pmem=,vsz=,rss= | while read cpu mem vsz rss; do
printf "%(%H:%M:%S)T %5.1f %5.1f %7d %6d\n" -1 $cpu $mem $vsz $rss
done
sleep $interval
done
}
# Start a process and monitor it
sleep 30 &
monitor_pid=$!
echo "Monitoring process $monitor_pid for 5 seconds..."
resource_monitor $monitor_pid 1 &
sleep 5
kill $monitor_pid
📜 Scripting
🎯 Complete Definition
Bash scripting involves writing sequences of commands in a file to automate tasks, create tools, and build complex workflows. Scripts combine all Bash features into reusable programs that can handle command-line arguments, interact with users, and perform system administration.
📋 Script Components
- Shebang: #!/bin/bash - Specifies interpreter
- Comments: # for documentation
- Variables: Store and manipulate data
- Functions: Reusable code blocks
- Control structures: Conditionals and loops
- Input/Output: Reading input, printing output
- Error handling: Exit codes, error messages
🔧 Script Features
- Command-line arguments: $1, $2, ..., $@, $*
- Options parsing: getopts for handling flags
- Exit codes: 0 for success, non-zero for errors
- Debugging: set -x, set -e, bash -x script.sh
- Strict mode: set -euo pipefail
- User input: read command
- Temporary files: mktemp
#!/bin/bash
# Bash Scripting - Complete Examples
# ===== STRICT MODE =====
# Exit on error, undefined variable, and pipe failure
set -euo pipefail
IFS=$'\n\t'
# ===== SCRIPT INFORMATION =====
echo "=== Script Information ==="
echo "Script name: $0"
echo "Script directory: $(dirname "$0")"
echo "Script PID: $$"
# ===== COMMAND LINE ARGUMENTS =====
echo "=== Command Line Arguments ==="
echo "Number of arguments: $#"
echo "All arguments: $@"
# ===== OPTIONS PARSING WITH GETOPTS =====
echo "=== Options Parsing ==="
usage() {
echo "Usage: $0 [-h] [-v] [-f file] [-n count]"
echo " -h Show this help"
echo " -v Verbose mode"
echo " -f Input file"
echo " -n Number (default: 10)"
exit 1
}
verbose=0
file=""
count=10
while getopts "hvf:n:" opt; do
case $opt in
h)
usage
;;
v)
verbose=1
;;
f)
file="$OPTARG"
;;
n)
count="$OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
;;
esac
done
echo "Verbose: $verbose"
echo "File: $file"
echo "Count: $count"
# ===== USER INPUT =====
echo "=== User Input ==="
# Simple prompt
read -p "Enter your name: " name
echo "Hello, $name!"
# Password input
read -s -p "Enter password: " password
echo
echo "Password length: ${#password}"
# Multiple values
read -p "Enter three numbers: " num1 num2 num3
echo "Numbers: $num1, $num2, $num3"
# ===== ERROR HANDLING =====
echo "=== Error Handling ==="
# Function with error checking
safe_divide() {
local dividend=$1
local divisor=$2
if [ $divisor -eq 0 ]; then
echo "Error: Division by zero" >&2
return 1
fi
echo $((dividend / divisor))
return 0
}
# Call with error checking
if result=$(safe_divide 10 2); then
echo "Result: $result"
else
echo "Division failed"
fi
# ===== TEMPORARY FILES =====
echo "=== Temporary Files ==="
# Create temp file
temp_file=$(mktemp)
echo "Temporary file: $temp_file"
# Write to temp file
cat > "$temp_file" < "$config_file" < /tmp/backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail
# Backup script
backup_dir="${HOME}/backups"
source_dir="${1:-.}"
max_backups=${2:-5}
# Create backup directory if needed
mkdir -p "$backup_dir"
# Create backup name
timestamp=$(date '+%Y%m%d_%H%M%S')
backup_name="backup_${timestamp}.tar.gz"
backup_path="${backup_dir}/${backup_name}"
echo "Creating backup of $source_dir -> $backup_path"
tar -czf "$backup_path" "$source_dir"
# Rotate old backups
cd "$backup_dir"
ls -t backup_*.tar.gz | tail -n +$((max_backups + 1)) | xargs -r rm
echo "Backup completed. $(ls -1 backup_*.tar.gz 2>/dev/null | wc -l) backups kept."
EOF
chmod +x /tmp/backup.sh
echo "Created /tmp/backup.sh"
}
create_backup_script
# ===== SELF-CONTAINED SCRIPT TEMPLATE =====
echo "=== Script Template ==="
cat <<'EOF' > /tmp/template.sh
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
# Script: template.sh
# Description: Template for bash scripts
# Author: CodeOrbitPro
# Version: 1.0
# Configuration
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_DIR=$(dirname "$0")
readonly VERSION="1.0"
# Default values
verbose=0
config_file=""
# Functions
usage() {
cat <&2
fi
}
# Parse arguments
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
;;
--version)
version
;;
-v|--verbose)
verbose=1
shift
;;
-c|--config)
config_file="$2"
shift 2
;;
--)
shift
break
;;
-*)
echo "Unknown option: $1" >&2
usage
;;
*)
break
;;
esac
done
# Main script
log "INFO" "Script started"
log "INFO" "Verbose mode: $verbose"
log "INFO" "Config file: $config_file"
log "INFO" "Remaining arguments: $*"
# Your script logic here
for arg in "$@"; do
log "INFO" "Processing argument: $arg"
done
log "INFO" "Script completed successfully"
EOF
chmod +x /tmp/template.sh
echo "Created /tmp/template.sh"
# ===== SCRIPT WRAPPER =====
echo "=== Script Wrapper ==="
# Wrapper that runs script with timeout and logging
run_with_wrapper() {
local script=$1
shift
local log_file="/tmp/wrapper_$$.log"
{
echo "=== Wrapper started at $(date) ==="
echo "Script: $script"
echo "Arguments: $*"
echo "=== Script output ==="
# Run with timeout
timeout 30 "$script" "$@" 2>&1
local exit_code=$?
echo "=== Script completed with exit code: $exit_code ==="
echo "=== Wrapper finished at $(date) ==="
} | tee "$log_file"
return $exit_code
}
# Test wrapper
echo "echo 'Hello from script'" > /tmp/test.sh
chmod +x /tmp/test.sh
run_with_wrapper /tmp/test.sh
rm /tmp/test.sh
# Cleanup
rm -f /tmp/backup.sh /tmp/template.sh
🔬 Advanced Bash
🎯 Complete Definition
Advanced Bash covers sophisticated techniques for writing robust, efficient, and maintainable shell scripts. This includes advanced expansions, coprocesses, named pipes, process substitution, and integration with other tools.
🔬 Advanced Features
- Brace Expansion: {1..10}, {a..z}, {file1,file2}.txt
- Process Substitution: <(command), >(command)
- Coprocesses: Two-way communication with processes
- Named Pipes (FIFOs): Inter-process communication
- Extended Globbing: Pattern matching extensions
- Indirect Expansion: ${!var}
- Parameter Transformation: ${var@Q}, ${var@E}, ${var@P}
- Associative Arrays: declare -A
- Loadable Builtins: Extending bash with C modules
⚡ Performance Optimization
- Builtins vs External: Use builtins when possible
- Minimize subshells: Avoid unnecessary pipes
- Use printf instead of echo: More portable and efficient
- Bash vs external tools: Know when to use awk/sed
- Parallel execution: Background jobs, xargs -P
#!/bin/bash
# Advanced Bash Programming
# ===== BRACE EXPANSION =====
echo "=== Brace Expansion ==="
# Number ranges
echo {1..10}
echo {01..10}
echo {1..10..2}
# Letter ranges
echo {a..z}
echo {A..Z}
echo {a..z..2}
# Combinations
echo {file1,file2,file3}.txt
echo {user1,user2}@{host1,host2}.com
# Nested brace expansion
echo {a,b{1,2,3},c}
# ===== PROCESS SUBSTITUTION =====
echo "=== Process Substitution ==="
# Compare command outputs
diff <(ls /tmp) <(ls /var/tmp) 2>/dev/null || echo "Directories differ"
# Feed multiple process outputs
paste <(seq 1 5) <(seq 10 15) <(seq 20 25)
# Process substitution for input
while read line; do
echo "Read: $line"
done < <(ls -la)
# Process substitution for output
exec > >(tee /tmp/output.log)
echo "This goes to both stdout and log file"
exec > /dev/tty
# ===== COPROCESSES =====
echo "=== Coprocesses ==="
# Create a coprocess
coproc bc {
bc -l
}
# Send input to coprocess
echo "scale=10; 4*a(1)" >&${COPROC[1]}
# Read output
read -u ${COPROC[0]} result
echo "Pi from bc: $result"
# Close coprocess
exec {COPROC[1]}>&-
exec {COPROC[0]}<&-
# ===== NAMED PIPES (FIFOs) =====
echo "=== Named Pipes ==="
# Create a named pipe
fifo="/tmp/myfifo.$$"
mkfifo "$fifo"
# Write to pipe in background
echo "Hello through pipe" > "$fifo" &
# Read from pipe
read line < "$fifo"
echo "Received: $line"
# Clean up
rm "$fifo"
# ===== EXTENDED GLOBBING =====
echo "=== Extended Globbing ==="
# Enable extended globbing
shopt -s extglob
# Pattern examples
echo "Extended globbing patterns:"
cd /tmp
# ?(pattern) - zero or one occurrence
echo "?(file).txt matches file.txt or .txt"
# *(pattern) - zero or more occurrences
echo "*(file).txt matches .txt, file.txt, filefile.txt"
# +(pattern) - one or more occurrences
echo "+(file).txt matches file.txt, filefile.txt"
# @(pattern1|pattern2) - exactly one of the patterns
echo "@(file|doc).txt matches file.txt or doc.txt"
# !(pattern) - anything but the pattern
echo "!(file).txt matches anything not file.txt"
# Examples
touch file.txt doc.txt data.txt
ls *.txt
ls @(file|doc).txt
ls !(file).txt
# Clean up
rm -f file.txt doc.txt data.txt
# Disable extended globbing
shopt -u extglob
# ===== INDIRECT EXPANSION =====
echo "=== Indirect Expansion ==="
var1="Hello"
var2="World"
ref="var1"
echo "Indirect expansion: ${!ref}"
# Dynamic variable names
for i in {1..5}; do
declare var$i="Value $i"
done
for i in {1..5}; do
ref="var$i"
echo "var$i = ${!ref}"
done
# ===== PARAMETER TRANSFORMATION =====
echo "=== Parameter Transformation ==="
text=$'Hello\nWorld\t!'
# Quote transformation
echo "@Q: ${text@Q}"
# Escape transformation
echo "@E: ${text@E}"
# Prompt transformation
PS1='\u@\h'
echo "@P: ${PS1@P}"
# ===== LOADABLE BUILTINS =====
echo "=== Loadable Builtins ==="
# Enable loadable builtins (if available)
# enable -f /path/to/builtin.so builtin_name
# Example: using 'cut' as builtin (not standard)
if enable -f /usr/lib/bash/cut cut 2>/dev/null; then
echo "cut builtin loaded"
cut -d: -f1 <<< "a:b:c"
else
echo "cut builtin not available"
fi
# ===== ASSOCIATIVE ARRAYS ADVANCED =====
echo "=== Advanced Associative Arrays ==="
declare -A data
# Store complex data
data["user1,name"]="John Doe"
data["user1,age"]=30
data["user1,city"]="New York"
data["user2,name"]="Jane Smith"
data["user2,age"]=25
data["user2,city"]="Los Angeles"
# Iterate by key pattern
for key in "${!data[@]}"; do
if [[ $key == user1,* ]]; then
echo "User1 $key = ${data[$key]}"
fi
done
# Nested data structure
declare -A users
users[1]="John:30:NYC"
users[2]="Jane:25:LA"
for id in "${!users[@]}"; do
IFS=':' read -r name age city <<< "${users[$id]}"
echo "User $id: $name, $age, $city"
done
# ===== ADVANCED REDIRECTION =====
echo "=== Advanced Redirection ==="
# Redirect multiple file descriptors
exec 3> /tmp/file3.txt
exec 4> /tmp/file4.txt
exec 5<&0 # Save stdin
echo "To file 3" >&3
echo "To file 4" >&4
# Restore and close
exec 3>&-
exec 4>&-
exec 0<&5 5<&-
# ===== NETWORK PROGRAMMING =====
echo "=== Network Programming ==="
# TCP/UDP redirection (bash 4+)
if [ -e /dev/tcp ]; then
# Simple HTTP request
exec 3<>/dev/tcp/example.com/80
echo -e "GET / HTTP/1.0\nHost: example.com\n\n" >&3
cat <&3 | head -5
exec 3>&-
fi
# ===== DEBUGGING TECHNIQUES =====
echo "=== Debugging Techniques ==="
# Trace execution
set -x
echo "This is traced"
x=5
y=10
echo "x + y = $((x + y))"
set +x
# Verbose mode
set -v
echo "This is verbose"
set +v
# Error checking
set -e # Exit on error
set -u # Exit on undefined variable
# Custom debug function
debug() {
if [ "${DEBUG:-0}" -eq 1 ]; then
echo "DEBUG: $*" >&2
fi
}
DEBUG=1
debug "This is a debug message"
# ===== PERFORMANCE OPTIMIZATION =====
echo "=== Performance Optimization ==="
# Time commands
time for i in {1..1000}; do
: # Null command
done
# Use builtins instead of externals
# Slow: $(cat file)
# Fast: $(> "$0"
echo "Script modified itself"
}
# 5. State machine example
declare -A state
state["current"]="START"
transition() {
local event=$1
case ${state["current"]} in
START)
if [ "$event" = "begin" ]; then
state["current"]="RUNNING"
echo "Started"
fi
;;
RUNNING)
if [ "$event" = "pause" ]; then
state["current"]="PAUSED"
elif [ "$event" = "end" ]; then
state["current"]="END"
echo "Finished"
fi
;;
PAUSED)
if [ "$event" = "resume" ]; then
state["current"]="RUNNING"
elif [ "$event" = "end" ]; then
state["current"]="END"
fi
;;
esac
}
transition "begin"
transition "pause"
transition "resume"
transition "end"
# 6. Meta-programming: create getters/setters
make_property() {
local var=$1
eval "
get_$var() {
echo \$$var
}
set_$var() {
$var=\$1
}
"
}
name=""
make_property name
set_name "John Doe"
echo "Name: $(get_name)"
# Cleanup
rm -f /tmp/file3.txt /tmp/file4.txt /tmp/temp_$$