Skip to content

Automation Scripts — Bash, Python, PowerShell

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn about Automation Scripts. We cover key concepts, practical examples, and best practices.

Automation scripts replace repetitive manual tasks with reliable, repeatable processes — whether you are renaming hundreds of files, deploying servers, or scanning for security threats across thousands of endpoints.

What You'll Learn

In this tutorial, you'll learn Bash scripting for Linux automation (file operations, process management, CRON), Python for cross-platform tasks (file processing, API automation, data pipelines), PowerShell for Windows administration (WMI/CIM, registry, Active Directory), and how to choose the right tool and schedule scripts with CRON, Task Scheduler, and CI/CD triggers.

Why It Matters

A single well-written automation script saves hours of manual work each week. System administrators, DevOps engineers, and security analysts who can automate their workflows are 10x more productive than those who perform tasks manually.

Real-World Use

Durga Antivirus Pro uses a suite of automation scripts for daily tasks: a Bash script that downloads, verifies, and imports new malware signatures; a Python script that scans S3 buckets for exposed credentials and reports findings to a Slack channel; and a PowerShell script that audits Windows endpoints for missing security patches.

flowchart LR
  A[Automation Scripts] --> B[Bash]
  A --> C[Python]
  A --> D[PowerShell]
  B --> E[Linux Admin]
  B --> F[CRON Jobs]
  C --> G[Cross-platform Tasks]
  C --> H[Data Pipelines]
  C --> I[API Integrations]
  D --> J[Windows Admin]
  D --> K[Active Directory]
  D --> L[Registry + WMI]

Bash Scripting for Linux Automation

File Processing with Error Handling

#!/bin/bash
# Script: rotate-logs.sh — Compress and archive logs older than 7 days
LOG_DIR="/var/log/scanner"
ARCHIVE_DIR="/var/log/archive"
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d)

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

if [ ! -d "$LOG_DIR" ]; then
    log "ERROR: Log directory $LOG_DIR does not exist"
    exit 1
fi

mkdir -p "$ARCHIVE_DIR"
find "$LOG_DIR" -name "*.log" -type f -mtime +$RETENTION_DAYS -print0 | \
    while IFS= read -r -d '' file; do
        gzip "$file"
        mv "${file}.gz" "$ARCHIVE_DIR/"
        log "Archived: $file"
    done

log "Log rotation complete"

Expected output: Log files older than 7 days are compressed with gzip and moved to the archive directory. Each archived file is logged with a timestamp.

CRON Schedule Integration

# Run every day at 3:00 AM
0 3 * * * /usr/local/bin/rotate-logs.sh >> /var/log/rotate-logs.log 2>&1
# Run security updates check every Monday at 4 AM
0 4 * * 1 /usr/local/bin/check-security-updates.sh
# Run health check every 5 minutes
*/5 * * * * /usr/local/bin/health-check.sh

Expected behavior: CRON executes each script at its scheduled time. Output (including errors) is redirected to log files for auditing and debugging.

Python for Cross-Platform Automation

API Automation with Error Handling

#!/usr/bin/env python3
"""check-endpoints.py — Verify API endpoints are healthy"""
import json
import sys
import urllib.request
import urllib.error

ENDPOINTS = [
    "https://api.dodabrowser.com/health",
    "https://api.dodazip.com/v1/health",
    "https://api.durga-antivirus.com/health",
]

def check_endpoint(url: str) -> dict:
    try:
        req = urllib.request.Request(url, method="GET")
        with urllib.request.urlopen(req, timeout=10) as response:
            return {
                "url": url,
                "status": response.status,
                "body": response.read().decode()[:200],
                "error": None,
            }
    except urllib.error.HTTPError as e:
        return {"url": url, "status": e.code, "body": None, "error": str(e)}
    except urllib.error.URLError as e:
        return {"url": url, "status": None, "body": None, "error": str(e.reason)}

def main():
    results = [check_endpoint(url) for url in ENDPOINTS]
    failures = [r for r in results if r["status"] != 200]

    print(json.dumps(results, indent=2))
    if failures:
        print(f"\nFAILURES: {len(failures)} endpoints returned non-200")
        sys.exit(1)
    print("\nAll endpoints healthy")

if __name__ == "__main__":
    main()

Expected output: JSON array with each endpoint's status code and response body (first 200 chars). If any endpoint returns non-200, the script exits with code 1 and lists failures.

File Processing with pandas

#!/usr/bin/env python3
"""merge-logs.py — Merge and analyze CSV log files"""
import pandas as pd
import glob
import sys

def merge_logs(pattern: str) -> pd.DataFrame:
    files = glob.glob(pattern)
    if not files:
        print(f"No files matching: {pattern}", file=sys.stderr)
        sys.exit(1)
    dataframes = [pd.read_csv(f) for f in files]
    merged = pd.concat(dataframes, ignore_index=True)
    return merged

def analyze_scans(df: pd.DataFrame) -> None:
    summary = df.groupby("status").agg(
        count=("file_hash", "count"),
        avg_size=("file_size", "mean"),
    ).reset_index()
    print("Scan Summary by Status:")
    print(summary.to_string(index=False))
    threats = df[df["threat_detected"] == True]
    print(f"\nThreats detected: {len(threats)}")
    print("Top threats:")
    print(threats["threat_name"].value_counts().head(5).to_string())

if __name__ == "__main__":
    df = merge_logs("scanner_logs_*.csv")
    analyze_scans(df)

Expected output: Grouped summary of scan statuses with counts and average file sizes. Number of detected threats and top 5 threat names with counts. Empty DataFrames cause early exit with a clear message.

PowerShell for Windows Administration

System Audit Script

# audit-system.ps1 — Audit Windows security settings
$report = @()

# Check Windows Defender status
$defender = Get-MpComputerStatus
$report += [PSCustomObject]@{
    Check = "Real-time Protection"
    Status = $defender.RealTimeProtectionEnabled
}

# Check installed updates
$updates = Get-HotFix | Where-Object {$_.InstalledOn -gt (Get-Date).AddDays(-30)}
$report += [PSCustomObject]@{
    Check = "Recent Updates (30 days)"
    Status = "$($updates.Count) updates installed"
}

# Check firewall rules
$firewall = Get-NetFirewallProfile -Profile Domain,Public,Private
foreach ($profile in $firewall) {
    $report += [PSCustomObject]@{
        Check = "Firewall: $($profile.Name)"
        Status = if ($profile.Enabled) { "Enabled" } else { "DISABLED" }
    }
}

$report | Format-Table -AutoSize
$report | Export-Csv -Path "security-audit-$((Get-Date).ToString('yyyyMMdd')).csv" -NoTypeInformation

Expected output: Formatted table showing real-time protection status, recent update count, and firewall status for each profile. CSV exported for further analysis. Any DISABLED items indicate security gaps.

Scheduled Task Creation

# Schedule the audit script to run weekly
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument "-File C:\Scripts\audit-system.ps1"
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At "6:00AM"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount
Register-ScheduledTask -TaskName "Weekly Security Audit" `
    -Action $action -Trigger $trigger -Principal $principal

Expected behavior: The PowerShell script runs every Monday at 6 AM as the SYSTEM account. The audit CSV is saved to the script's directory with the current date in the filename.

Scripting Language Comparison

Language Platform Best For Scheduling Package Ecosystem
Bash Linux/macOS File ops, process mgmt, pipes CRON apt/yum (system packages)
Python All Data processing, APIs, cross-platform CRON, Task Scheduler pip (PyPI, 400k+ packages)
PowerShell Windows (also Linux/macOS) Windows admin, AD, registry, WMI Task Scheduler PSGallery (PowerShell Gallery)
Node.js All Web scraping, API automation CRON, pm2 npm (2M+ packages)

Common Errors

  1. Not handling edge cases in Bash — Unset variables cause silent failures. Use set -u to error on unset variables and set -e to exit on any command failure.
  2. Python script fails with encoding errors on non-ASCII filenames — Always use encoding="utf-8" when opening files, and use pathlib instead of os.path for cross-platform path handling.
  3. PowerShell execution policy blocking scripts — By default, PowerShell restricts script execution. Use Set-ExecutionPolicy RemoteSigned -Scope CurrentUser to allow local scripts.
  4. Hardcoding paths and credentials — Use environment variables (Bash/Python), config files (Python), or secure strings (PowerShell) instead of hardcoding paths, passwords, and API keys.
  5. Not testing scripts in a sandbox first — A script that deletes files in production should first be tested in a sandbox with --dry-run or -WhatIf flags. Always validate before destructive operations.

Practice Questions

  1. When should you use Python over Bash for automation? Python is better for complex logic, cross-platform compatibility, data processing (CSV, JSON, XML), API integrations, and any task requiring third-party libraries.

  2. How do you pass arguments to a Bash script? Use $1, $2, etc. for positional arguments, $@ for all arguments, and $# for the argument count. Use getopts for named flags.

  3. What is the PowerShell equivalent of grep in Bash? Select-String (alias: sls). Example: Get-Content log.txt | Select-String "error" is equivalent to grep "error" log.txt.

  4. How do you schedule a Python script to run daily? On Linux: CRON. On Windows: Task Scheduler. Cross-platform: schedule library in Python, or systemd timers on Linux.

Challenge

Write a tri-lingual automation suite: a Bash script that finds and archives files older than 30 days (with dry-run mode), an equivalent Python script that also generates a summary report as a CSV, and an equivalent PowerShell script that audits Windows servers. Compare the implementations and note platform-specific considerations.

Mini Project: Automated Threat Detection Pipeline

Build an automated pipeline that simulates Durga Antivirus Pro's threat intelligence workflow:

  1. Bash script: Download the latest threat feed CSV from a mock URL, verify the file checksum, and extract it
  2. Python script: Parse the CSV, cross-reference against a local database of known signatures, identify new threats not yet in the database, and generate a report
  3. PowerShell script (Windows): Check if any new threats correspond to running processes on the system and quarantine them
  4. Schedule the pipeline: CRON on Linux (every 4 hours), Task Scheduler on Windows (daily)
  5. Add logging: each step writes to a centralized log file with timestamps and exit codes
  6. Add alerting: if a critical threat is found, send a notification via email (or Slack webhook)

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro