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

1. What You'll Learn


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:

BASH
#!/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!"
💡 Tip: 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:

TEXT
#!/bin/bash      # Execute with bash
#!/bin/sh        # Execute with sh (compatibility mode)
#!/usr/bin/env python3  # Execute with Python 3
💡 Tip: #!/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

TEXT
# 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, $variable and $(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).

TEXT
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

TEXT
./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 / [[ ]])

TEXT
# 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

BASH
# 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

BASH
#!/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:

TEXT
Hello, World!
Current user: alice
Current directory: /home/alice
File count: 12

(7) ▶ Example: Using Positional Parameters

TEXT
#!/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: $#"
TEXT
./greet.sh Alice
# Hello, Alice!

(8) ▶ Example: if Conditional

TEXT
#!/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

TEXT
#!/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

TEXT
#!/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

BASH
#!/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/bash on 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 — run chmod +x script.sh. 2) The current directory is not in PATH — use ./script.sh instead of script.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 with for arg in "$@"; do is the standard practice.

Q: What's the difference between exit 0 and exit 1? A: exit 0 means success; exit non-zero means 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 read command: read -p "Enter your name: " name, then $name contains the user's input.


📖 Summary


📝 Exercises

  1. Basic (⭐): Write a script info.sh that outputs the current username, date, current directory, and file count
  2. Intermediate (⭐⭐): Write a script check-file.sh that takes a filename argument, checks if the file exists and outputs its type. Then write a script backup-txt.sh that backs up all .txt files in the current directory to /tmp/txt-backup/
  3. Challenge (⭐⭐⭐): Write a countdown script that takes a seconds argument and counts down each second, add set -e error handling. Then use a for loop to create 10 directories named test-1 through test-10, and finally write a script that calculates and sorts the sizes of those 10 directories
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏