Linux: Bash Scripting Basics — Variables / Conditions / Loops
Save a series of Linux commands to a file, add some logic control (conditions, loops), and you have a Shell script. It's the starting point for automating all repetitive work.
📋 Prerequisites: You should already know
- Lesson 19: Archives and Compression
1. What You'll Learn
- Shebang and execute permissions
- Variable definition and quote differences
- Positional parameters
- if/test conditional logic
- for/while loops
2. A Story from Manual Deployment to One-Click Deploy
(1) The Pain Point: Typing the Same 5 Commands Every Day
Xiaoming had to run these commands every day to deploy: git pull, npm install, npm run build, pm2 restart app, echo "done". 5 commands, 3 minutes, repeated every day.
(2) An 8-Line Script
He wrote a deploy.sh:
#!/bin/bash
set -e
echo "Starting deployment..."
cd /var/www/myapp
git pull origin main
npm install
npm run build
pm2 restart app
echo "✅ Deployment complete!"
set -e is the first line of defense for script safety — the script stops immediately if any command returns a non-zero exit code. Without set -e, the script continues running even after a command fails, which can cause serious cascading errors (like restarting a service with broken code).
(3) The Benefit: One-Click Deployment
From then on, Xiaoming only needed to run ./deploy.sh — all steps completed automatically, saving time and avoiding omissions.
3. Knowledge Points
(1) Shebang
The first line of a script must be the Shebang — telling the system which interpreter to use:
#!/bin/bash # Execute with bash
#!/bin/sh # Execute with sh (compatibility mode)
#!/usr/bin/env python3 # Execute with Python 3
#!/bin/bash is the Shebang — it tells the OS which interpreter to use for the script. Without this line, the system uses the current shell, which may cause compatibility issues (e.g., sh doesn't support [[ ]] syntax). All bash scripts must start with #!/bin/bash.
(2) Variables
# Define variables (no spaces around the equals sign)
name="Alice"
age=25
# Use variables (curly braces recommended)
echo $name # Alice
echo ${name} # Alice (braces are safer)
echo "Hello, ${name}"
# Variable naming rules
# 1. Letters, digits, underscores
# 2. Cannot start with a digit
# 3. Case-sensitive (NAME != name)
Three Types of Quotes:
| Quote | Example | Behavior |
|---|---|---|
| Double quotes | "Hello $name" |
Parses variables and commands |
| Single quotes | 'Hello $name' |
Literal string, no parsing |
Backticks/$() |
`date` or $(date) |
Execute command, capture output |
ℹ️ Note: The core difference between single and double quotes — in double quotes,
$variableand$(command)are parsed and replaced; everything in single quotes is literal. Use double quotes when interpolating variables, single quotes when you need to preserve$and other special characters (e.g., in regex patterns or heredocs where you don't want variable expansion).
name="Alice"
echo "Hello $name" # Hello Alice
echo 'Hello $name' # Hello $name
echo "Today is $(date)" # Today is Tue Jul 7 10:23:45 CST 2026
(3) Positional Parameters
./script.sh arg1 arg2 arg3
# $1 $2 $3
$0 # Script name itself
$1, $2 # 1st, 2nd argument
$# # Number of arguments
$@ # All arguments (as separate words)
$* # All arguments (as a single string)
$? # Exit code of previous command (0=success, non-0=failure)
$$ # PID of current script
(4) Conditional Logic (if / test / [[ ]])
# test command or [ ]
if [ "$name" = "Alice" ]; then
echo "Hello Alice"
fi
# More powerful [[ ]] (bash-specific)
if [[ "$name" == "Alice" ]]; then
echo "Hello Alice"
fi
# if-elif-else
if [ "$1" -gt 10 ]; then
echo "Greater than 10"
elif [ "$1" -gt 5 ]; then
echo "Greater than 5"
else
echo "5 or less"
fi
File Tests:
| Condition | Meaning |
|---|---|
-f file |
Is a regular file |
-d dir |
Is a directory |
-e path |
Exists |
-s file |
File size > 0 |
-r file |
Is readable |
-w file |
Is writable |
-x file |
Is executable |
String Comparison:
| Condition | Meaning |
|---|---|
$a = $b |
Equal |
$a != $b |
Not equal |
-z $a |
String is empty (length = 0) |
-n $a |
String is not empty |
Numeric Comparison:
| Condition | Meaning |
|---|---|
$a -eq $b |
Equal |
$a -ne $b |
Not equal |
$a -gt $b |
Greater than |
$a -ge $b |
Greater than or equal |
$a -lt $b |
Less than |
$a -le $b |
Less than or equal |
(5) Loops
# for loop
for i in 1 2 3 4 5; do
echo "Number: $i"
done
for file in *.txt; do
echo "Found file: $file"
done
for i in {1..10}; do
echo "$i"
done
# while loop
count=1
while [ $count -le 5 ]; do
echo "Attempt $count"
count=$((count + 1))
done
# Read file line by line
while IFS= read -r line; do
echo "$line"
done < /etc/hostname
(6) ▶ Example: Basic Script
#!/bin/bash
# hello.sh - First bash script
echo "Hello, World!"
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo "File count: $(ls | wc -l)"
Output:
TEXTHello, World! Current user: alice Current directory: /home/alice File count: 12
(7) ▶ Example: Using Positional Parameters
#!/bin/bash
# greet.sh - Greet a specified user
if [ $# -eq 0 ]; then
echo "Usage: $0 <username>"
exit 1
fi
echo "Hello, $1!"
echo "Welcome to Linux scripting."
echo "Script name: $0"
echo "Argument count: $#"
./greet.sh Alice
# Hello, Alice!
(8) ▶ Example: if Conditional
#!/bin/bash
# check-file.sh - Check file status
FILE="${1:-test.txt}"
if [ ! -e "$FILE" ]; then
echo "File '$FILE' does not exist"
exit 1
fi
if [ -f "$FILE" ]; then
echo "Type: Regular file"
elif [ -d "$FILE" ]; then
echo "Type: Directory"
fi
[ -r "$FILE" ] && echo "Readable: ✅" || echo "Readable: ❌"
[ -w "$FILE" ] && echo "Writable: ✅" || echo "Writable: ❌"
[ -x "$FILE" ] && echo "Executable: ✅" || echo "Executable: ❌"
(9) ▶ Example: for Loop Batch Operation
#!/bin/bash
# rename.sh - Batch rename .txt to .md
count=0
for file in *.txt; do
if [ -f "$file" ]; then
mv "$file" "${file%.txt}.md"
echo "Renamed: $file → ${file%.txt}.md"
count=$((count + 1))
fi
done
echo "Total files renamed: $count"
(10) ▶ Example: while Loop and Reading Input
#!/bin/bash
# countdown.sh - Countdown timer
if [ $# -ne 1 ]; then
echo "Usage: $0 <seconds>"
exit 1
fi
count=$1
while [ $count -gt 0 ]; do
echo "Countdown: $count"
count=$((count - 1))
sleep 1
done
echo "Time's up!"
(11) ▶ Comprehensive Example: Batch File Processing Script
#!/bin/bash
# batch-process.sh - Batch process files in a directory
DIR="${1:-.}"
ACTION="${2:-info}"
if [ ! -d "$DIR" ]; then
echo "Error: Directory '$DIR' does not exist"
exit 1
fi
echo "Processing directory: $DIR"
echo "Action: $ACTION"
echo "---"
case "$ACTION" in
info)
echo "File statistics:"
echo " Total files: $(find "$DIR" -type f | wc -l)"
echo " Total directories: $(find "$DIR" -type d | wc -l)"
echo " Total size: $(du -sh "$DIR" | cut -f1)"
;;
backup)
BACKUP="/tmp/backup-$(date +%Y%m%d)"
mkdir -p "$BACKUP"
cp -r "$DIR" "$BACKUP/"
echo "Backup complete: $BACKUP"
;;
clean)
echo "Cleaning .log files..."
find "$DIR" -name "*.log" -delete
echo "Cleaning .tmp files..."
find "$DIR" -name "*.tmp" -delete
echo "Cleaning complete"
;;
*)
echo "Actions: info / backup / clean"
;;
esac
❓ FAQ
Q: Is the
#!/bin/bashon the first line mandatory? A: Yes. It tells the kernel which interpreter to use. Without it, the default is the current shell (which may cause compatibility issues).
Q: Why does my script say
command not found? A: Two reasons: 1) No execute permission — runchmod +x script.sh. 2) The current directory is not in PATH — use./script.shinstead ofscript.sh.
Q: What's the difference between
$@and$*? A:$@treats each argument as a separate "word";$*combines all arguments into a single string. Most scenarios recommend$@. Iterating withfor arg in "$@"; dois the standard practice.
Q: What's the difference between
exit 0andexit 1? A:exit 0means success;exit non-zeromeans failure (different numbers indicate different error types). Other programs can check$?to determine if the script succeeded.
Q: How to get user input in a script? A: Use the
readcommand:read -p "Enter your name: " name, then$namecontains the user's input.
📖 Summary
- Shebang
#!/bin/bash,chmod +xgrants execute permission - Variables:
name="value", reference with$nameor${name} - Double quotes parse variables, single quotes are literal,
$(cmd)executes a command if [ condition ]for conditional logic,for i in listfor loopsexit 0for success,exit 1for error
📝 Exercises
- Basic (⭐): Write a script
info.shthat outputs the current username, date, current directory, and file count - Intermediate (⭐⭐): Write a script
check-file.shthat takes a filename argument, checks if the file exists and outputs its type. Then write a scriptbackup-txt.shthat backs up all.txtfiles in the current directory to/tmp/txt-backup/ - Challenge (⭐⭐⭐): Write a countdown script that takes a seconds argument and counts down each second, add
set -eerror handling. Then use aforloop to create 10 directories namedtest-1throughtest-10, and finally write a script that calculates and sorts the sizes of those 10 directories