What Is a Script?
A script is a file containing a sequence of commands that a computer executes automatically. Instead of typing commands one by one, you write them in a file, and the computer runs them in order. Scripts automate repetitive tasks, from simple file operations to complex system administration workflows.
Think of a script as a recipe: you write down the steps once, and anyone (or any computer) can follow them to achieve the same result. This is the foundation of automation in IT, DevOps, and software development.
Scripts vs. Programs: What's the Difference?
While the terms are sometimes used interchangeably, there are key differences:
| Aspect | Scripts | Compiled Programs |
|---|---|---|
| Execution | Interpreted line-by-line | Compiled to machine code first |
| Speed | Slower execution | Faster execution |
| Development | Quick to write and test | Longer development cycle |
| Use Case | Automation, glue code | Applications, system software |
| Examples | Bash, Python, PowerShell | C, C++, Rust, Go |
Scripts excel at automating tasks, connecting systems, and rapid prototyping. When you need to automate something quickly, scripting is usually the answer.
Why Learn Scripting?
Scripting skills are valuable across many roles:
For System Administrators:
- Automate user account creation and management
- Schedule backups and maintenance tasks
- Monitor system health and generate alerts
- Deploy software across multiple servers
For Developers:
- Automate build and deployment processes
- Create development environment setup scripts
- Generate boilerplate code and project structures
- Run automated tests
For Data Analysts:
- Process and transform data files
- Automate report generation
- Schedule data imports and exports
- Clean and validate datasets
For Security Professionals:
- Automate security scans and audits
- Parse and analyze log files
- Create incident response automation
- Generate compliance reports
Choosing a Scripting Language
The best scripting language depends on your operating system, use case, and existing skills.
Bash (Linux/macOS)
Bash (Bourne Again Shell) is the default shell on most Linux distributions and macOS. It's ideal for:
- System administration on Unix-like systems
- File and directory manipulation
- Text processing with tools like
grep,sed, andawk - Automating command-line workflows
#!/bin/bash
# Simple backup script
SOURCE="/home/user/documents"
BACKUP="/backup/documents-$(date +%Y%m%d).tar.gz"
echo "Starting backup..."
tar -czf "$BACKUP" "$SOURCE"
echo "Backup complete: $BACKUP"
Pros: Pre-installed on most systems, excellent for system tasks, pipes and redirects are powerful Cons: Syntax can be cryptic, limited data structures, not ideal for complex logic
PowerShell (Windows)
PowerShell is Microsoft's modern shell and scripting language. It's built on .NET and works with objects rather than text:
- Windows system administration
- Active Directory management
- Azure and Microsoft 365 automation
- Cross-platform (PowerShell Core runs on Linux/macOS)
# Get all stopped services and start them
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Start-Service
# Create a new user in Active Directory
New-ADUser -Name "John Doe" -GivenName "John" -Surname "Doe" `
-SamAccountName "jdoe" -UserPrincipalName "[email protected]" `
-Path "OU=Users,DC=domain,DC=com" -AccountPassword (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) `
-Enabled $true
Pros: Object-oriented output, excellent Windows integration, strong typing available Cons: Verbose syntax, learning curve for object pipeline
Python
Python is a general-purpose language that excels at scripting due to its readability and extensive libraries:
- Cross-platform automation
- Data processing and analysis
- Web scraping and API integration
- Complex logic and algorithms
#!/usr/bin/env python3
"""Organize files by extension into folders."""
import os
import shutil
from pathlib import Path
def organize_downloads(directory):
"""Move files into folders based on their extension."""
extensions = {
'Images': ['.jpg', '.jpeg', '.png', '.gif', '.svg'],
'Documents': ['.pdf', '.doc', '.docx', '.txt', '.xlsx'],
'Videos': ['.mp4', '.avi', '.mkv', '.mov'],
'Archives': ['.zip', '.tar', '.gz', '.rar']
}
for file in Path(directory).iterdir():
if file.is_file():
for folder, exts in extensions.items():
if file.suffix.lower() in exts:
dest = Path(directory) / folder
dest.mkdir(exist_ok=True)
shutil.move(str(file), str(dest / file.name))
print(f"Moved {file.name} to {folder}/")
break
if __name__ == "__main__":
organize_downloads("/home/user/Downloads")
Pros: Readable syntax, massive library ecosystem, cross-platform, great for complex tasks Cons: Requires installation, slower than compiled languages
How to Write Your First Script
Let's walk through writing a script step by step, using Bash as an example.
Step 1: Create the Script File
Open a text editor and create a new file. Script files typically have extensions that indicate their language:
- Bash:
.sh - Python:
.py - PowerShell:
.ps1
# Create a new file
touch my_first_script.sh
# Open in your editor
nano my_first_script.sh
Step 2: Add the Shebang Line
The first line of a script should be the shebang (or hashbang), which tells the system which interpreter to use:
#!/bin/bash
Common shebangs:
#!/bin/bash- Bash shell#!/usr/bin/env python3- Python 3 (portable)#!/usr/bin/env node- Node.js
Step 3: Write Your Commands
Add the commands you want to execute. Start simple:
#!/bin/bash
# My first script
# This script displays system information
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "User: $(whoami)"
echo "Date: $(date)"
echo "Uptime: $(uptime -p)"
echo "========================="
Step 4: Make It Executable
Before running your script, you need to make it executable:
chmod +x my_first_script.sh
Step 5: Run Your Script
Execute your script:
./my_first_script.sh
Output:
=== System Information ===
Hostname: server01
User: admin
Date: Fri Jan 3 10:30:00 UTC 2025
Uptime: up 15 days, 4 hours
===========================
Essential Scripting Concepts
Variables
Variables store data for reuse throughout your script:
# Bash variables
NAME="John"
AGE=30
echo "Hello, $NAME. You are $AGE years old."
# Command output in a variable
CURRENT_DIR=$(pwd)
FILE_COUNT=$(ls | wc -l)
# Python variables
name = "John"
age = 30
print(f"Hello, {name}. You are {age} years old.")
# Store command output
import subprocess
current_dir = subprocess.check_output(['pwd']).decode().strip()
Conditional Logic (If Statements)
Make decisions in your scripts:
#!/bin/bash
FILE="/etc/passwd"
if [ -f "$FILE" ]; then
echo "$FILE exists"
else
echo "$FILE does not exist"
fi
# Check disk usage
USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$USAGE" -gt 80 ]; then
echo "WARNING: Disk usage is above 80%"
fi
import os
file_path = "/etc/passwd"
if os.path.exists(file_path):
print(f"{file_path} exists")
else:
print(f"{file_path} does not exist")
Loops
Repeat actions multiple times:
#!/bin/bash
# For loop - iterate over a list
for server in web01 web02 web03; do
echo "Pinging $server..."
ping -c 1 "$server"
done
# While loop - repeat until condition is false
COUNT=1
while [ $COUNT -le 5 ]; do
echo "Iteration $COUNT"
COUNT=$((COUNT + 1))
done
# Process each line in a file
while read -r line; do
echo "Processing: $line"
done < servers.txt
# For loop
servers = ['web01', 'web02', 'web03']
for server in servers:
print(f"Pinging {server}...")
# While loop
count = 1
while count <= 5:
print(f"Iteration {count}")
count += 1
# Process file lines
with open('servers.txt', 'r') as f:
for line in f:
print(f"Processing: {line.strip()}")
Functions
Organize code into reusable blocks:
#!/bin/bash
# Define a function
backup_directory() {
local source=$1
local dest=$2
if [ -d "$source" ]; then
tar -czf "$dest" "$source"
echo "Backup created: $dest"
return 0
else
echo "Error: $source does not exist"
return 1
fi
}
# Call the function
backup_directory "/var/log" "/backup/logs-$(date +%Y%m%d).tar.gz"
def backup_directory(source: str, dest: str) -> bool:
"""Create a compressed backup of a directory."""
import tarfile
import os
if os.path.isdir(source):
with tarfile.open(dest, "w:gz") as tar:
tar.add(source)
print(f"Backup created: {dest}")
return True
else:
print(f"Error: {source} does not exist")
return False
# Call the function
backup_directory("/var/log", "/backup/logs-20250103.tar.gz")
Error Handling
Handle errors gracefully to make scripts robust:
#!/bin/bash
set -e # Exit on any error
set -u # Treat unset variables as errors
# Check if command succeeded
if ! command -v python3 &> /dev/null; then
echo "Error: Python 3 is not installed"
exit 1
fi
# Trap errors
cleanup() {
echo "Cleaning up temporary files..."
rm -f /tmp/script_temp_*
}
trap cleanup EXIT
import sys
try:
with open('config.json', 'r') as f:
config = json.load(f)
except FileNotFoundError:
print("Error: config.json not found")
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in config file: {e}")
sys.exit(1)
Learn more about Python error handling in our guide: Python Try Except Explained.
Script Best Practices
1. Add Comments
Document what your script does and why:
#!/bin/bash
# ==============================================================================
# Script Name: backup_databases.sh
# Description: Creates daily backups of all MySQL databases
# Author: Your Name
# Date: 2025-01-03
# Usage: ./backup_databases.sh [--full|--incremental]
# ==============================================================================
# Configuration
BACKUP_DIR="/backup/mysql" # Where to store backups
RETENTION_DAYS=30 # How long to keep backups
2. Use Meaningful Variable Names
# Bad
d="/backup"
n=30
# Good
BACKUP_DIRECTORY="/backup"
RETENTION_DAYS=30
3. Handle Arguments
Accept command-line arguments for flexibility:
#!/bin/bash
# Default values
VERBOSE=false
OUTPUT_FILE=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-v|--verbose)
VERBOSE=true
shift
;;
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
-h|--help)
echo "Usage: $0 [-v] [-o output_file]"
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
4. Validate Input
Never trust user input:
#!/bin/bash
# Validate required argument
if [ -z "$1" ]; then
echo "Error: Please provide a filename"
exit 1
fi
# Validate file exists
if [ ! -f "$1" ]; then
echo "Error: File '$1' not found"
exit 1
fi
# Validate numeric input
if ! [[ "$2" =~ ^[0-9]+$ ]]; then
echo "Error: Second argument must be a number"
exit 1
fi
5. Log Output
Record what your script does:
#!/bin/bash
LOG_FILE="/var/log/my_script.log"
log() {
local level=$1
local message=$2
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE"
}
log "INFO" "Script started"
log "INFO" "Processing files..."
log "ERROR" "Failed to connect to database"
log "INFO" "Script completed"
6. Test Before Production
- Test in a development environment first
- Use
set -xin Bash to see commands as they execute - Start with
echobefore destructive commands - Create backups before modifying data
#!/bin/bash
# Dry run mode - echo instead of execute
DRY_RUN=${DRY_RUN:-false}
delete_old_files() {
if [ "$DRY_RUN" = true ]; then
echo "[DRY RUN] Would delete: $1"
else
rm -f "$1"
fi
}
Scheduling Scripts with Cron
Once you've written a script, you'll often want to run it automatically. On Linux/macOS, use cron to schedule scripts:
# Edit your crontab
crontab -e
# Run backup script daily at 2 AM
0 2 * * * /home/user/scripts/backup.sh
# Run cleanup every Sunday at midnight
0 0 * * 0 /home/user/scripts/cleanup.sh
# Run health check every 5 minutes
*/5 * * * * /home/user/scripts/healthcheck.sh
Use our Cron Expression Builder to create and validate cron schedules.
Common Scripting Mistakes to Avoid
1. Not Quoting Variables
# Wrong - breaks with spaces in filename
rm $FILE
# Right - handles spaces correctly
rm "$FILE"
2. Ignoring Exit Codes
# Wrong - continues even if command fails
cp source dest
process dest
# Right - check for errors
cp source dest || { echo "Copy failed"; exit 1; }
process dest
3. Hardcoding Values
# Wrong - hardcoded paths
tar -czf /backup/data.tar.gz /var/data
# Right - use variables
BACKUP_DIR="${BACKUP_DIR:-/backup}"
SOURCE_DIR="${SOURCE_DIR:-/var/data}"
tar -czf "$BACKUP_DIR/data.tar.gz" "$SOURCE_DIR"
4. Running as Root Unnecessarily
Only use elevated privileges when absolutely necessary:
#!/bin/bash
# Check if running as root when needed
if [ "$EUID" -ne 0 ]; then
echo "This script requires root privileges"
echo "Please run with: sudo $0"
exit 1
fi
Real-World Script Examples
System Health Check Script
#!/bin/bash
# health_check.sh - Monitor system resources
check_disk() {
local threshold=80
local usage=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$usage" -gt "$threshold" ]; then
echo "WARNING: Disk usage is ${usage}% (threshold: ${threshold}%)"
return 1
fi
echo "OK: Disk usage is ${usage}%"
return 0
}
check_memory() {
local threshold=80
local usage=$(free | grep Mem | awk '{print int($3/$2 * 100)}')
if [ "$usage" -gt "$threshold" ]; then
echo "WARNING: Memory usage is ${usage}% (threshold: ${threshold}%)"
return 1
fi
echo "OK: Memory usage is ${usage}%"
return 0
}
check_load() {
local threshold=4
local load=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | tr -d ' ')
local load_int=${load%.*}
if [ "$load_int" -gt "$threshold" ]; then
echo "WARNING: Load average is $load (threshold: $threshold)"
return 1
fi
echo "OK: Load average is $load"
return 0
}
echo "=== System Health Check ==="
echo "Date: $(date)"
echo ""
check_disk
check_memory
check_load
echo ""
echo "=== Check Complete ==="
Log Rotation Script
#!/bin/bash
# rotate_logs.sh - Compress and archive old log files
LOG_DIR="/var/log/myapp"
ARCHIVE_DIR="/backup/logs"
DAYS_TO_KEEP=30
# Create archive directory if needed
mkdir -p "$ARCHIVE_DIR"
# Compress logs older than 1 day
find "$LOG_DIR" -name "*.log" -mtime +1 -exec gzip {} \;
# Move compressed logs to archive
find "$LOG_DIR" -name "*.log.gz" -exec mv {} "$ARCHIVE_DIR/" \;
# Delete archives older than retention period
find "$ARCHIVE_DIR" -name "*.log.gz" -mtime +$DAYS_TO_KEEP -delete
echo "Log rotation complete: $(date)"
Next Steps
Now that you understand the basics of writing scripts:
- Practice daily: Automate one small task each day
- Read others' scripts: Learn from open-source projects
- Learn a second language: If you know Bash, try Python; if you know PowerShell, try Bash
- Build a script library: Save and organize useful scripts for reuse
- Version control: Use Git to track changes to your scripts
Related Resources:
- Cron Expression Builder - Schedule your scripts
- Python Try Except Guide - Error handling in Python
- Python Switch Statements - Conditional logic patterns
- What is a Cron Job? - Understanding scheduled tasks
Conclusion
Learning how to write scripts is one of the most valuable skills you can develop in IT. Scripts save time, reduce errors, and enable you to automate repetitive tasks that would otherwise consume hours of manual work. Start with simple scripts that solve real problems you face daily, and gradually build complexity as your skills grow.
The key to becoming proficient at scripting is practice. Every system administrator, developer, and DevOps engineer started with their first "Hello World" script. Pick a language, solve a problem, and keep building. Before long, you'll wonder how you ever worked without automation.