Shell Scripting
bash Β· variables Β· loops Β· functions Β· pipes Β· redirection Β· grep Β· sed Β· awk Β· cron
Sheet 3 of 3
Bash 5.x
Beginner
Printable
Script Basics & Variables
Shebang & Running Scripts
#!/bin/bash # β shebang β which interpreter to use # Also common: #!/usr/bin/env bash # Make script executable chmod +x script.sh # Run it ./script.sh bash script.sh # or explicitly # Print to terminal echo "Hello, World!" printf "%s is %d years old\n" "Alice" 25 # Comments # This is a single-line comment
Variables & Quoting
# Assign (no spaces around =) NAME="Alice" AGE=25 GREETING="Hello" # Read variable (use $) echo "$NAME" # Alice echo "${NAME}" # Alice (safer) echo "$GREETING, $NAME!" # Hello, Alice! # readonly β cannot be changed readonly PI=3.14159 # unset β delete variable unset AGE # Single quotes β no interpolation echo '$NAME' # $NAME (literal) # Command substitution TODAY=$(date +%Y-%m-%d) FILES=$(ls -1 | wc -l)
Special Variables
# Script arguments $0 # script name $1 # first argument $2 # second argument $@ # all arguments (array) $# # number of arguments # Process & status $$ # current PID $! # PID of last background job $? # exit code of last command # 0 = success, non-zero = error # Usage example #!/bin/bash echo "Script: $0" echo "First arg: $1" echo "All args: $@" echo "Count: $#" # Environment variables echo $HOME # /home/user echo $USER # username echo $PATH # executable search path echo $PWD # current directory
Always quote variables: Use
"$VAR" not $VAR to prevent word splitting and glob expansion when the value contains spaces or special characters.Conditionals
if / elif / else
AGE=20 if [[ $AGE -ge 18 ]]; then echo "Adult" elif [[ $AGE -ge 13 ]]; then echo "Teen" else echo "Child" fi # Numeric comparisons # -eq equal # -ne not equal # -lt less than # -le less than or equal # -gt greater than # -ge greater than or equal # String comparisons [[ $A == $B ]] # equal [[ $A != $B ]] # not equal [[ -z $A ]] # empty string [[ -n $A ]] # non-empty string
File Tests & case
# File test operators [[ -f "file.txt" ]] # is a regular file [[ -d "/tmp" ]] # is a directory [[ -e "path" ]] # exists [[ -r "file" ]] # readable [[ -w "file" ]] # writable [[ -x "file" ]] # executable [[ -s "file" ]] # not empty (size>0) # Logical operators [[ $A == 1 && $B == 2 ]] [[ $A == 1 || $B == 2 ]] [[ ! -f "file" ]] # NOT # case statement case $DAY in Mon|Tue|Wed|Thu|Fri) echo "Weekday" ;; Sat|Sun) echo "Weekend" ;; *) echo "Unknown" ;; esac
Loops
for loops
# for-in β iterate list for fruit in apple mango cherry; do echo "$fruit" done # C-style for loop for ((i=0; i<5; i++)); do echo "$i" done # Range with brace expansion for i in {1..5}; do echo "$i" done # Iterate files for file in *.txt; do echo "Processing: $file" done # Iterate command output for user in $(cat users.txt); do echo "Hello $user" done
while Β· until Β· break Β· continue
# while β loop while true COUNT=1 while [[ $COUNT -le 5 ]]; do echo "Count: $COUNT" ((COUNT++)) done # Read file line by line while IFS= read -r line; do echo "$line" done < file.txt # until β loop while false until [[ $COUNT -gt 5 ]]; do ((COUNT++)) done # break and continue for i in {1..10}; do [[ $i -eq 3 ]] && continue # skip 3 [[ $i -eq 7 ]] && break # stop at 7 echo $i done
Functions
Define & Call
# Function definition greet() { echo "Hello, $1!" } # Alternative syntax function say_bye { echo "Goodbye, $1!" } # Call a function greet "Alice" # Hello, Alice! say_bye "Bob" # Goodbye, Bob! # Function must be defined before use # Arguments: $1, $2, $@, $#
Return Values & Local Variables
# Return exit code (0=ok, 1=error) is_even() { [[ $(($1 % 2)) -eq 0 ]] } if is_even 4; then echo "even" fi # Return a value via echo add() { echo $(($1 + $2)) } RESULT=$(add 3 5) echo $RESULT # 8 # local β function-scoped variable my_func() { local TMP="only inside" echo $TMP }
Useful Patterns
# Validate argument count require_args() { if [[ $# -lt 2 ]]; then echo "Usage: $0 <a> <b>" >&2 return 1 fi } # Default parameter value connect() { local HOST=${1:-"localhost"} local PORT=${2:-8080} echo "Connecting $HOST:$PORT" } connect # localhost:8080 connect myserver # myserver:8080 connect db 5432 # db:5432
Pipes & Redirection
Redirection
# stdout β file (overwrite) echo "Hello" > output.txt # stdout β file (append) echo "World" >> output.txt # stdin β file sort < names.txt # stderr β file ls /bad/path 2> errors.log # stdout + stderr β file command > all.log 2>&1 command &> all.log # shorthand # Discard output command > /dev/null 2>&1 # Here document cat <<EOF Line 1 Line 2 EOF
Pipes & xargs
# | pipes stdout of one β stdin of next ls -la | grep ".txt" cat file.txt | sort | uniq ps aux | grep nginx # tee β pipe AND write to file ls -la | tee files.log # xargs β pipe list β command args find . -name "*.log" | xargs rm cat urls.txt | xargs curl -O # Chain commands mkdir newdir && cd newdir # run if prev ok cd /bad || echo "failed" # run if prev fails cmd1 ; cmd2 # run always
Arrays & Arithmetic
Arrays
# Create array FRUITS=("apple" "mango" "cherry") # Access echo ${FRUITS[0]} # apple echo ${FRUITS[@]} # all elements echo ${#FRUITS[@]} # count = 3 # Modify FRUITS+=("kiwi") # append FRUITS[1]="banana" # replace index 1 unset FRUITS[2] # remove index 2 # Loop over array for f in "${FRUITS[@]}"; do echo $f done
Arithmetic & String Operations
# Arithmetic A=10; B=3 echo $((A + B)) # 13 echo $((A - B)) # 7 echo $((A * B)) # 30 echo $((A / B)) # 3 (integer) echo $((A % B)) # 1 ((A++)) # increment # String operations S="Hello World" echo ${#S} # length = 11 echo ${S:0:5} # Hello (substr) echo ${S,,} # hello world (lower) echo ${S^^} # HELLO WORLD (upper) echo ${S/World/Bash} # Hello Bash echo ${S#Hello } # World (strip prefix)
grep Β· sed Β· awk
grep β search text
# Basic search grep "error" app.log # Common flags grep -i "error" app.log # case insensitive grep -n "error" app.log # show line numbers grep -c "error" app.log # count matches grep -v "debug" app.log # invert (exclude) grep -r "TODO" ./src/ # recursive grep -l "error" *.log # filenames only grep -A2 "error" app.log # 2 lines after grep -B2 "error" app.log # 2 lines before # Extended regex (egrep) grep -E "error|warn" app.log grep -E "^[0-9]{4}" dates.txt # Fixed string (faster, no regex) grep -F "[ERROR]" app.log
sed β stream editor
# Substitute (replace) sed 's/old/new/' file.txt # first only sed 's/old/new/g' file.txt # global sed 's/old/new/gi' file.txt # case-insensitive # Edit in-place sed -i 's/foo/bar/g' file.txt sed -i.bak 's/foo/bar/g' file.txt # backup # Delete lines sed '/pattern/d' file.txt # matching lines sed '2d' file.txt # line 2 sed '2,5d' file.txt # lines 2-5 # Print specific lines sed -n '5p' file.txt # line 5 only sed -n '5,10p' file.txt # lines 5-10 # Insert / Append sed '2i\Inserted line' file.txt # before line 2 sed '2a\Appended line' file.txt # after line 2
awk β column & pattern processing
# Print specific column awk '{print $1}' file.txt # col 1 awk '{print $2, $4}' file.txt # Custom delimiter awk -F: '{print $1}' /etc/passwd awk -F, '{print $1}' data.csv # Pattern matching awk '/error/{print}' app.log awk '$3 > 100' data.txt # col 3 > 100 # Built-in variables # NR = line number # NF = number of fields awk '{print NR, $0}' file.txt # add line nums awk '{print $NF}' file.txt # last column # Sum a column awk '{sum += $2} END {print sum}' data.txt # BEGIN / END blocks awk 'BEGIN{print "Start"} {print} END{print "End"}' f
Quick pipeline recipe:
grep "ERROR" app.log | awk '{print $1, $4}' | sort | uniq -c | sort -rn | head -10 β find the top 10 most frequent errors with timestamps.Cron Jobs
Crontab syntax
# Edit crontab crontab -e # edit current user's crons crontab -l # list current crons crontab -r # remove all crons # Format: min hr dom mon dow command # βββ minute (0-59) # β βββ hour (0-23) # β β βββ day of month (1-31) # β β β βββ month (1-12) # β β β β βββ day of week (0-7, 0&7=Sun) # β β β β β # * * * * * command # Examples 0 9 * * 1 /backup.sh # 9am every Monday 30 2 * * * /cleanup.sh # 2:30am every day * * * * * /script.sh # every minute 0 0 1 * * /monthly.sh # midnight, 1st of month 0 * * * * /hourly.sh # every hour 0 0 * * 0 /weekly.sh # midnight every Sunday
Shortcuts & Best Practices
# Special strings @reboot /start.sh # on system boot @hourly /hourly.sh # = 0 * * * * @daily /daily.sh # = 0 0 * * * @weekly /weekly.sh # = 0 0 * * 0 @monthly /monthly.sh # = 0 0 1 * * @yearly /yearly.sh # = 0 0 1 1 * # Ranges and steps 0 9-17 * * * /cmd.sh # every hr 9-17 */15 * * * * /cmd.sh # every 15 mins 0 0 * * 1,3,5 /cmd.sh # Mon,Wed,Fri # Log output 0 2 * * * /backup.sh >> /var/log/backup.log 2>&1 # Use full paths β cron has minimal PATH 0 1 * * * /usr/bin/python3 /home/user/job.py
Essential Commands
find & file operations
# find files find . -name "*.log" # by name find . -type f -newer ref.txt # newer than find . -size +10M # larger than 10MB find . -mtime -7 # modified <7 days find . -name "*.tmp" -delete # find + delete find . -name "*.sh" -exec chmod +x {} \; # Useful text commands wc -l file.txt # count lines wc -w file.txt # count words head -n 20 file.txt # first 20 lines tail -n 20 file.txt # last 20 lines tail -f app.log # follow live sort file.txt # sort lines sort -r file.txt # reverse sort sort -n file.txt # numeric sort uniq file.txt # remove duplicates uniq -c file.txt # count occurrences
cut Β· tr Β· paste Β· join
# cut β extract columns cut -d: -f1 /etc/passwd # field 1 (: delim) cut -d, -f1,3 data.csv # fields 1 and 3 cut -c1-10 file.txt # characters 1-10 # tr β translate characters echo "hello" | tr a-z A-Z # HELLO echo "a:b:c" | tr ':' ',' # a,b,c echo "hello" | tr -d l # heo (delete l) echo " a b " | tr -s ' ' # squeeze spaces # Practical pipeline cat access.log | awk '{print $1}' | # extract IPs sort | # sort IPs uniq -c | # count unique sort -rn | # top first head -10 # top 10 IPs
Error Handling & Best Practices
Strict mode β safer scripts
#!/bin/bash # Put at top of every script set -e # exit on any error set -u # error on undefined vars set -o pipefail # pipe fails if any cmd fails # Shorthand for all three set -euo pipefail # Check exit code manually cp file.txt /backup/ if [[ $? -ne 0 ]]; then echo "Backup failed" >&2 exit 1 fi # Or use || for inline check cp file.txt /backup/ || { echo "Backup failed" >&2 exit 1 }
trap β cleanup on exit
#!/bin/bash set -euo pipefail # Create temp file TMP=$(mktemp) # Cleanup function cleanup() { echo "Cleaning up..." rm -f "$TMP" } # Register trap: run cleanup on... trap cleanup EXIT # always on exit trap cleanup ERR # on error trap cleanup INT # on Ctrl+C trap cleanup TERM # on kill signal # Trap multiple signals at once trap cleanup EXIT ERR INT TERM # Script body echo "Working..." > "$TMP" # cleanup() always runs at end
Script template & logging
#!/bin/bash set -euo pipefail # Logging helpers log() { echo "[INFO] $*"; } warn() { echo "[WARN] $*" >&2; } error() { echo "[ERROR] $*" >&2; } die() { error "$@"; exit 1; } # Usage / help usage() { echo "Usage: $0 <input> <output>" exit 1 } [[ $# -lt 2 ]] && usage log "Starting script" # ... script body ... log "Done"
Always use set -euo pipefail at the top of every script. Without it, bash silently ignores errors and continues β which can cause data loss or corrupted state. Also always write to
stderr with >&2 for error messages.Shell Scripting Mastery Checklist
| Basics & Variables | Key point |
|---|---|
| Write a valid shebang line | #!/bin/bash |
| Assign and read variables | VAR=val / "$VAR" |
| Use special variables | $1 $@ $# $? $$ |
| Capture command output | VAR=$(command) |
| Write conditionals correctly | [[ ]] with -eq -gt -z |
| Write for / while loops | for i in ... / while [[ ]] |
| Pipes & Text Tools | Key point |
|---|---|
| Redirect stdout to file | > (overwrite) / >> (append) |
| Redirect stderr | 2> errors.log |
| Pipe commands together | cmd1 | cmd2 | cmd3 |
| Search with grep | grep -inrv "pattern" file |
| Replace text with sed | sed 's/old/new/g' file |
| Extract columns with awk | awk '{print $2}' file |
| Functions & Safety | Key point |
|---|---|
| Define and call a function | fn() { ... } / fn args |
| Use local variables | local VAR=value |
| Enable strict mode | set -euo pipefail |
| Register cleanup with trap | trap cleanup EXIT ERR |
| Schedule a cron job | crontab -e / min hr * * * |
| Use cron shortcuts | @daily @weekly @reboot |
Other Languages series complete! Β·
You've covered Scala Β· Dart Β· Shell Scripting. Explore more series on Dataplexa β Python, Java, Rust, Data Science, AWS Cloud, and more.