← Tech Guides
Shell Scripting

Bash Scripting

Complete reference guide for Bash shell scripting. Master variables, conditionals, loops, functions, I/O redirection, and advanced scripting patterns.

Quick Reference

Essential constructs at a glance for quick lookup and daily scripting.

# Variables
var="value"                    # Assignment (no spaces around =)
echo "$var"                    # Use with $
readonly CONST="immutable"     # Read-only variable
export PATH="/usr/bin:$PATH"   # Export to environment

# Conditionals
if [[ condition ]]; then
    commands
elif [[ condition ]]; then
    commands
else
    commands
fi

# Loops
for item in list; do commands; done
while [[ condition ]]; do commands; done
until [[ condition ]]; do commands; done

# Functions
function_name() {
    local var="$1"
    echo "$var"
}

# Common Tests
[[ -f file ]]      # File exists
[[ -d dir ]]       # Directory exists
[[ -z "$var" ]]    # String is empty
[[ "$a" == "$b" ]] # String equality
[[ $num -eq 5 ]]   # Numeric equality

# I/O Redirection
cmd > file         # Redirect stdout to file
cmd >> file        # Append stdout to file
cmd 2> file        # Redirect stderr to file
cmd &> file        # Redirect both stdout and stderr
cmd < file         # Redirect file to stdin
cmd1 | cmd2        # Pipe stdout of cmd1 to stdin of cmd2

Variables & Expansion

Variable Declaration

# Basic assignment (no spaces around =)
name="John Doe"
count=42
empty=""

# Command substitution
current_dir=$(pwd)
files=$(ls -1)
date_old=`date`  # Deprecated style, use $() instead

# Read-only variables
readonly PI=3.14159
declare -r MAX_SIZE=1000

# Unsetting variables
unset var_name

Export and Environment

# Export to child processes
export PATH="/usr/local/bin:$PATH"
export DB_HOST="localhost"

# Export with declaration
export VAR="value"

# List all exported variables
export -p

# Temporary environment variable (one command only)
FOO=bar command args

Parameter Expansion

var="hello"

# Basic expansion
echo "$var"           # hello
echo "${var}"         # hello (preferred in complex expressions)

# Default values
echo "${undefined:-default}"      # Use default if undefined or empty
echo "${undefined-default}"       # Use default only if undefined
echo "${var:-fallback}"           # hello (var is set)

# Assign default
echo "${new_var:=assigned}"       # Assigns and returns "assigned"

# Alternative value (use if set)
echo "${var:+alternative}"        # alternative (var is set)

# Error if undefined
echo "${must_exist:?Error: variable not set}"

# String length
echo "${#var}"                    # 5

# Substring extraction
text="Hello World"
echo "${text:0:5}"               # Hello (offset 0, length 5)
echo "${text:6}"                 # World (offset 6 to end)
echo "${text: -5}"               # World (last 5 chars, note space)

# Pattern removal (shortest match)
path="/home/user/file.tar.gz"
echo "${path#*/}"                # home/user/file.tar.gz
echo "${path%/*}"                # /home/user

# Pattern removal (longest match)
echo "${path##*/}"               # file.tar.gz (basename)
echo "${path%%/*}"               # (empty)

# Pattern replacement
text="Hello World"
echo "${text/World/Universe}"    # Replace first
echo "${text//o/0}"              # Replace all
echo "${text/#Hello/Hi}"         # Replace at start
echo "${text/%World/Earth}"      # Replace at end

# Case conversion (Bash 4+)
echo "${text,,}"                 # hello world (lowercase all)
echo "${text^^}"                 # HELLO WORLD (uppercase all)

Arrays (Indexed)

# Declaration
fruits=("apple" "banana" "cherry")
empty_array=()

# Alternative declaration
declare -a array
array[0]="first"
array[1]="second"

# Access elements
echo "${fruits[0]}"              # apple
echo "${fruits[@]}"              # All elements

# Array length
echo "${#fruits[@]}"             # 3 (number of elements)

# Array slicing
echo "${fruits[@]:1}"            # banana cherry (from index 1)
echo "${fruits[@]:1:1}"          # banana (from index 1, length 1)

# Append to array
fruits+=("date")

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

# Iterate with indices
for i in "${!fruits[@]}"; do
    echo "Index $i: ${fruits[$i]}"
done

Associative Arrays (Bash 4+)

# Declaration (must declare explicitly)
declare -A colors
colors[red]="#FF0000"
colors[green]="#00FF00"
colors[blue]="#0000FF"

# Alternative declaration
declare -A person=(
    [name]="John"
    [age]="30"
    [city]="NYC"
)

# Access elements
echo "${colors[red]}"            # #FF0000

# Check if key exists
if [[ -v colors[red] ]]; then
    echo "Key exists"
fi

# All keys
echo "${!colors[@]}"             # red green blue

# Iterate over keys and values
for key in "${!colors[@]}"; do
    echo "$key: ${colors[$key]}"
done

Conditionals

If/Elif/Else Structure

# Basic if
if [[ condition ]]; then
    commands
fi

# If-else
if [[ condition ]]; then
    commands
else
    commands
fi

# If-elif-else
if [[ condition1 ]]; then
    commands
elif [[ condition2 ]]; then
    commands
else
    commands
fi

String Comparisons

str1="hello"
str2="world"

# Equality
[[ "$str1" == "hello" ]]         # True
[[ "$str1" != "world" ]]         # True

# Pattern matching (only with [[)
[[ "$str1" == h* ]]              # True (glob pattern)

# Regex matching (only with [[)
[[ "$str1" =~ ^he.* ]]           # True (regex)

# Lexicographic comparison
[[ "$str1" < "$str2" ]]          # True (hello < world)

# Empty/non-empty
[[ -z "$empty" ]]                # True (string is empty)
[[ -n "$str1" ]]                 # True (string is not empty)

Numeric Comparisons

a=10
b=20

# Arithmetic comparisons
[[ $a -eq 10 ]]                  # Equal
[[ $a -ne 20 ]]                  # Not equal
[[ $a -lt 20 ]]                  # Less than
[[ $a -le 10 ]]                  # Less than or equal
[[ $a -gt 5 ]]                   # Greater than
[[ $a -ge 10 ]]                  # Greater than or equal

# Arithmetic evaluation (( ))
if (( a < b )); then
    echo "a is less than b"
fi

# Arithmetic with assignment
(( a++ ))                        # Increment
(( a += 5 ))                     # Add 5
(( result = a * b ))             # Multiply

File Tests

Test Description Example
-e Exists (file or directory) [[ -e "$file" ]]
-f Regular file exists [[ -f "$file" ]]
-d Directory exists [[ -d "$dir" ]]
-L Symbolic link exists [[ -L "$link" ]]
-r Readable [[ -r "$file" ]]
-w Writable [[ -w "$file" ]]
-x Executable [[ -x "$file" ]]
-s File exists and is not empty [[ -s "$file" ]]
-nt Newer than [[ file1 -nt file2 ]]
-ot Older than [[ file1 -ot file2 ]]

Logical Operators

# AND
[[ condition1 && condition2 ]]
[[ -f file.txt && -r file.txt ]]

# OR
[[ condition1 || condition2 ]]
[[ -f file1 || -f file2 ]]

# NOT
[[ ! condition ]]
[[ ! -f file.txt ]]

# Combining
[[ ( condition1 || condition2 ) && condition3 ]]

Case Statement

case "$var" in
    pattern1)
        commands
        ;;
    pattern2)
        commands
        ;;
    *)
        default commands
        ;;
esac

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

# Pattern matching
case "$filename" in
    *.txt)
        echo "Text file"
        ;;
    *.jpg|*.png|*.gif)
        echo "Image file"
        ;;
    [Mm]akefile)
        echo "Makefile"
        ;;
    *)
        echo "Unknown file type"
        ;;
esac

Loops

For Loops

# List iteration
for item in one two three; do
    echo "$item"
done

# Array iteration
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
    echo "$fruit"
done

# Glob expansion (files)
for file in *.txt; do
    echo "Processing $file"
done

# Brace expansion
for i in {1..10}; do
    echo "Number $i"
done

for letter in {a..z}; do
    echo "$letter"
done

C-Style For Loop

# C-style syntax
for (( i=0; i<10; i++ )); do
    echo "$i"
done

# Multiple variables
for (( i=0, j=10; ido
    echo "i=$i, j=$j"
done

While Loop

# Basic while
count=0
while [[ $count -lt 5 ]]; do
    echo "$count"
    (( count++ ))
done

# Read from file line by line
while IFS= read -r line; do
    echo "Line: $line"
done < file.txt

# Infinite loop
while true; do
    echo "Running..."
    sleep 1
done

Until Loop

# Runs until condition becomes true
count=0
until [[ $count -ge 5 ]]; do
    echo "$count"
    (( count++ ))
done

# Wait for file to appear
until [[ -f /tmp/ready ]]; do
    echo "Waiting..."
    sleep 1
done

Loop Control

# Break (exit loop)
for i in {1..10}; do
    if [[ $i -eq 5 ]]; then
        break
    fi
    echo "$i"
done

# Continue (skip to next iteration)
for i in {1..10}; do
    if [[ $i -eq 5 ]]; then
        continue
    fi
    echo "$i"  # Skips 5
done

Functions

Function Declaration

# Method 1: function keyword
function my_function {
    echo "Hello from function"
}

# Method 2: POSIX style (preferred)
my_function() {
    echo "Hello from function"
}

# Call function
my_function

Function Arguments

# Access arguments
greet() {
    echo "Hello, $1!"        # First argument
    echo "Second arg: $2"    # Second argument
}

greet "Alice" "Bob"

# Special variables
show_args() {
    echo "Script name: $0"
    echo "First arg: $1"
    echo "All args: $@"
    echo "Arg count: $#"
    echo "Last exit code: $?"
    echo "PID: $$"
}

Return Values

# Return exit status (0-255)
is_even() {
    local num=$1
    if (( num % 2 == 0 )); then
        return 0  # Success (true)
    else
        return 1  # Failure (false)
    fi
}

if is_even 4; then
    echo "Even"
fi

# Return string via stdout
get_user() {
    echo "john_doe"
}

user=$(get_user)
echo "User: $user"

Local Variables

# With local (function scope)
good_function() {
    local var="I'm local"
    echo "$var"
}

good_function   # I'm local
echo "$var"     # (empty, var is local to function)

# Multiple local declarations
calculate() {
    local a=$1
    local b=$2
    local result=$(( a + b ))
    echo "$result"
}

Recursion

# Factorial
factorial() {
    local n=$1
    if (( n <= 1 )); then
        echo 1
    else
        local prev=$(factorial $(( n - 1 )))
        echo $(( n * prev ))
    fi
}

result=$(factorial 5)
echo "5! = $result"  # 120

String Manipulation

Substring Removal (Pattern Matching)

path="/home/user/documents/file.tar.gz"

# Remove shortest match from beginning (#)
echo "${path#*/}"        # home/user/documents/file.tar.gz

# Remove longest match from beginning (##)
echo "${path##*/}"       # file.tar.gz (basename)

# Remove shortest match from end (%)
echo "${path%/*}"        # /home/user/documents (dirname)

# Remove longest match from end (%%)
echo "${path%%.*}"       # /home/user/documents/file

# Extension manipulation
filename="document.txt"
echo "${filename%.*}"    # document (remove extension)
echo "${filename##*.}"   # txt (get extension)

Pattern Replacement

text="Hello World, Hello Universe"

# Replace first occurrence (/)
echo "${text/Hello/Hi}"              # Hi World, Hello Universe

# Replace all occurrences (//)
echo "${text//Hello/Hi}"             # Hi World, Hi Universe

# Replace at beginning (/#)
echo "${text/#Hello/Hi}"             # Hi World, Hello Universe

# Replace at end (/%)
echo "${text/%Universe/Galaxy}"      # Hello World, Hello Galaxy

# Delete pattern (replace with empty)
echo "${text//Hello/}"               # World, Universe

Substring Extraction

text="Hello World"

# Syntax: ${var:offset:length}
echo "${text:0:5}"       # Hello (start at 0, length 5)
echo "${text:6:5}"       # World (start at 6, length 5)
echo "${text:6}"         # World (start at 6, to end)

# Negative offset (from end)
echo "${text: -5}"       # World (last 5 chars, note space)

# Get first/last characters
echo "${text:0:1}"       # H (first char)
echo "${text: -1}"       # d (last char)

Case Conversion (Bash 4+)

text="Hello World"

# Lowercase
echo "${text,,}"         # hello world (all lowercase)
echo "${text,}"          # hello World (first char lowercase)

# Uppercase
echo "${text^^}"         # HELLO WORLD (all uppercase)
echo "${text^}"          # Hello World (first char uppercase)

String Splitting

# Using IFS (Internal Field Separator)
text="one,two,three"
IFS=',' read -ra parts <<< "$text"
echo "${parts[0]}"       # one
echo "${parts[1]}"       # two

# Split into variables
IFS=':' read -r user pass <<< "john:secret"
echo "User: $user"       # john
echo "Pass: $pass"       # secret

I/O & Redirection

Output Redirection

# Redirect stdout to file (overwrite)
echo "text" > file.txt
ls -l > listing.txt

# Redirect stdout to file (append)
echo "more text" >> file.txt
date >> log.txt

# Redirect stderr to file
command 2> error.log

# Redirect both stdout and stderr to file
command &> output.txt
command > output.txt 2>&1    # Equivalent

# Discard output
command > /dev/null          # Discard stdout
command 2> /dev/null         # Discard stderr
command &> /dev/null         # Discard both

Input Redirection

# Redirect file to stdin
command < input.txt
wc -l < file.txt

# Read from file
while read -r line; do
    echo "$line"
done < file.txt

Heredoc (Here Document)

# Basic heredoc
cat << EOF
This is a multi-line
text block that ends
with EOF
EOF

# Variable expansion in heredoc
name="Alice"
cat << EOF
Hello, $name!
Current directory: $(pwd)
EOF

# Suppress variable expansion (quote delimiter)
cat << 'EOF'
This is $literal text
$(pwd) is not expanded
EOF

# Heredoc to file
cat > config.txt << EOF
setting1=value1
setting2=value2
EOF

Here String (<<<)

# Pass string as stdin
grep "pattern" <<< "text to search"
wc -w <<< "count these words"

# With variable
text="hello world"
tr ' ' '\n' <<< "$text"

# Read into variable
read -r var <<< "value"
echo "$var"              # value

Pipes

# Pipe stdout to next command
ls -l | grep ".txt"
cat file.txt | sort | uniq

# Multiple pipes
ps aux | grep python | grep -v grep | awk '{print $2}'

# Tee (write to file and stdout)
command | tee output.txt      # Overwrite
command | tee -a output.txt   # Append

Process Substitution

# Compare output of two commands
diff <(ls dir1) <(ls dir2)

# Read command output line by line
while read -r line; do
    echo "$line"
done < <(find . -name "*.txt")

Process Management

Background Jobs

# Run in background
command &

# Get job PID
sleep 60 &
echo $!                      # PID of last background job

# Store PID
long_task &
pid=$!
echo "Running with PID: $pid"

Job Control

Command Description
jobs List all jobs
jobs -l List jobs with PIDs
fg Bring last job to foreground
fg %1 Bring job number 1 to foreground
bg Send last stopped job to background
bg %1 Send job number 1 to background
kill %1 Kill job number 1
disown Remove last job from job table

Wait for Background Jobs

# Wait for all background jobs
command1 &
command2 &
command3 &
wait
echo "All done"

# Wait for specific PID
long_task &
pid=$!
wait $pid
echo "Task complete"

# Parallel execution with wait
for i in {1..5}; do
    (
        echo "Task $i starting"
        sleep $((RANDOM % 5))
        echo "Task $i done"
    ) &
done
wait

Trap (Signal Handling)

# Trap signal and execute command
trap 'echo "Caught signal"' SIGINT SIGTERM

# Trap script exit
trap 'echo "Script exiting"' EXIT

# Cleanup on exit
cleanup() {
    echo "Cleaning up..."
    rm -f /tmp/tempfile.*
}
trap cleanup EXIT

# Common pattern: cleanup temporary files
tmpfile=$(mktemp)
trap "rm -f $tmpfile" EXIT

Signals

Signal Number Description
SIGINT 2 Interrupt (Ctrl+C)
SIGTERM 15 Terminate (default kill)
SIGKILL 9 Kill (cannot be caught)
SIGHUP 1 Hangup
SIGQUIT 3 Quit (Ctrl+\)
# Send signal
kill -SIGTERM $pid
kill -15 $pid
kill $pid                    # Default: SIGTERM

Process Information

# Current process ID
echo $$

# Parent process ID
echo $PPID

# Last background job PID
command &
echo $!

# Exit status of last command
command
echo $?

Script Patterns

Shebang

#!/bin/bash                  # Use bash
#!/usr/bin/env bash          # Find bash in PATH (portable)
#!/bin/sh                    # POSIX shell (limited features)
#!/bin/bash -e               # Exit on error
#!/bin/bash -x               # Debug mode

Strict Mode (Bash Strict Mode)

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

# -e: Exit on error
# -u: Exit on undefined variable
# -o pipefail: Pipeline fails if any command fails
# IFS: Set safe Internal Field Separator

Set Options

Option Short Description
set -e errexit Exit on error
set -u nounset Exit on undefined variable
set -o pipefail - Pipeline fails if any command fails
set -x xtrace Print commands before execution
set -f noglob Disable filename expansion

Script Template

#!/usr/bin/env bash
#
# Script: script_name.sh
# Description: What this script does
#

set -euo pipefail
IFS=$'\n\t'

# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

# Variables
verbose=false
dry_run=false

# Functions
usage() {
    cat << EOF
Usage: $SCRIPT_NAME [OPTIONS] ARGS

Description of what the script does.

OPTIONS:
    -h, --help       Show this help message
    -v, --verbose    Verbose output
    -n, --dry-run    Dry run mode

EXAMPLES:
    $SCRIPT_NAME -v file.txt
EOF
    exit 0
}

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
}

error() {
    echo "[ERROR] $*" >&2
    exit 1
}

cleanup() {
    log "Cleaning up..."
}

trap cleanup EXIT

# Main script
main() {
    log "Script starting"
    # Your code here
    log "Script complete"
}

main "$@"

Getopts (Argument Parsing)

# Basic getopts
while getopts "hvo:" opt; do
    case $opt in
        h)
            usage
            ;;
        v)
            verbose=true
            ;;
        o)
            output="$OPTARG"
            ;;
        \?)
            echo "Invalid option: -$OPTARG" >&2
            exit 1
            ;;
    esac
done

# Shift to positional arguments
shift $((OPTIND - 1))
echo "Remaining args: $*"

Temporary Files

# Create temp file
tmpfile=$(mktemp)
echo "Temp file: $tmpfile"

# Create temp directory
tmpdir=$(mktemp -d)

# Cleanup temp file
trap "rm -f $tmpfile" EXIT

# Temp file with template
tmpfile=$(mktemp /tmp/myapp.XXXXXX)

Error Handling

# Custom error handler
error_exit() {
    echo "Error: $1" >&2
    exit 1
}

[[ -f "$file" ]] || error_exit "File not found: $file"

# Try-catch pattern
if ! command; then
    echo "Command failed" >&2
    exit 1
fi

# Error trap
handle_error() {
    echo "Error on line $1" >&2
    exit 1
}
trap 'handle_error $LINENO' ERR

Checking Prerequisites

# Check if command exists
command -v git &> /dev/null || {
    echo "Error: git is not installed" >&2
    exit 1
}

# Check multiple commands
for cmd in git curl jq; do
    command -v "$cmd" &> /dev/null || {
        echo "Error: $cmd is not installed" >&2
        exit 1
    }
done

# Check if root
[[ $EUID -eq 0 ]] || {
    echo "Error: This script must be run as root" >&2
    exit 1
}

Text Processing

Grep

# Basic search
grep "pattern" file.txt

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

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

# Invert match (exclude lines)
grep -v "pattern" file.txt

# Recursive search
grep -r "pattern" /path/to/dir

# Context lines
grep -A 3 "pattern" file.txt  # 3 lines after
grep -B 3 "pattern" file.txt  # 3 lines before
grep -C 3 "pattern" file.txt  # 3 lines before and after

Sed (Stream Editor)

# Substitute (first occurrence per line)
sed 's/old/new/' file.txt

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

# In-place edit
sed -i 's/old/new/g' file.txt

# Delete lines
sed '/pattern/d' file.txt        # Delete matching lines
sed '5d' file.txt                # Delete line 5
sed '$d' file.txt                # Delete last line

# Print lines
sed -n '5p' file.txt             # Print line 5
sed -n '/pattern/p' file.txt     # Print matching lines

Awk

# Print columns
awk '{print $1}' file.txt        # First column
awk '{print $1, $3}' file.txt    # Columns 1 and 3
awk '{print $NF}' file.txt       # Last column

# Custom delimiter
awk -F':' '{print $1}' /etc/passwd

# Pattern matching
awk '/pattern/ {print $1}' file.txt

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

# Count lines
awk 'END {print NR}' file.txt

Sort & Uniq

# Basic sort
sort file.txt

# Reverse sort
sort -r file.txt

# Numeric sort
sort -n numbers.txt

# Sort by column
sort -k2 file.txt                # Sort by 2nd column

# Unique sort
sort -u file.txt                 # Sort and remove duplicates

# Remove duplicate consecutive lines
uniq file.txt

# Count occurrences
uniq -c file.txt

Tr (Translate)

# Character translation
echo "hello" | tr 'a-z' 'A-Z'    # Lowercase to uppercase

# Delete characters
echo "hello123" | tr -d '0-9'    # Remove digits

# Squeeze repeats
echo "hello    world" | tr -s ' '  # Single space

# Replace newlines with spaces
tr '\n' ' ' < file.txt

Xargs

# Basic usage
echo "file1 file2 file3" | xargs rm

# One argument at a time
echo "1 2 3" | xargs -n1 echo

# Replace string
echo "file1 file2" | xargs -I {} mv {} {}.bak

# Parallel execution
cat urls.txt | xargs -P 4 -n1 curl -O

# Handle spaces in filenames
find . -name "*.txt" -print0 | xargs -0 rm

Pro Tips

ShellCheck

ShellCheck is a static analysis tool for shell scripts that finds bugs, security issues, and poor practices.

# Check script
shellcheck script.sh

# Ignore specific warnings in script:
# shellcheck disable=SC2086
variable=$unquoted
Install ShellCheck

Ubuntu/Debian: apt install shellcheck
macOS: brew install shellcheck

Debugging

# Print commands before execution
set -x
bash -x script.sh

# Custom debug output
set -x
PS4='+ ${BASH_SOURCE}:${LINENO}: '

# Debug function
debug() {
    [[ $DEBUG ]] && echo "[DEBUG] $*" >&2
}

# Usage
DEBUG=1 bash script.sh

# DryRun pattern
DRY_RUN=false

run() {
    if $DRY_RUN; then
        echo "Would run: $*"
    else
        "$@"
    fi
}

Common Gotchas

Word Splitting

Always quote variables to prevent word splitting on spaces.

# WRONG: splits on spaces
for file in $files; do
    echo "$file"
done

# CORRECT:
for file in "${files[@]}"; do
    echo "$file"
done
Pipe Subshell

Variables set in pipes run in subshells and don't affect the parent.

# WRONG: read runs in subshell
echo "value" | read var
echo "$var"  # Empty!

# CORRECT: use here string
read var <<< "value"
echo "$var"  # value
Test [ vs [[

Use [[ ]] for modern Bash. It's safer and supports more features.

# WRONG with [ ]
[ -f $file ]      # Breaks if file contains spaces

# CORRECT with [[ ]]
[[ -f $file ]]    # No quotes needed

Performance Tips

# Use built-ins instead of external commands
# SLOW:
basename=$(basename "$path")
dirname=$(dirname "$path")

# FAST:
basename="${path##*/}"
dirname="${path%/*}"

# SLOW:
length=$(echo "$var" | wc -c)

# FAST:
length="${#var}"

# Use mapfile/readarray for large files
# FAST:
mapfile -t lines < file.txt

Useful One-Liners

# Find and replace in multiple files
find . -name "*.txt" -exec sed -i 's/old/new/g' {} +

# Find large files
find / -type f -size +100M 2>/dev/null

# Backup with timestamp
cp file.txt "file.txt.$(date +%Y%m%d_%H%M%S).bak"

# Get process using most memory
ps aux | sort -rk4 | head -1

# Create directory and cd into it
mkcd() { mkdir -p "$1" && cd "$1"; }

# Extract any archive
extract() {
    case "$1" in
        *.tar.gz) tar xzf "$1" ;;
        *.zip) unzip "$1" ;;
        *.tar.bz2) tar xjf "$1" ;;
        *) echo "Unknown format" ;;
    esac
}

# Generate random password
openssl rand -base64 32

# Get external IP
curl -s ifconfig.me

# Batch rename files
for file in *.txt; do mv "$file" "${file%.txt}.md"; done

Resources