Writing Reusable Bash Functions for WordPress Automation

If you’re writing WordPress automation scripts with WP-CLI, you’ve probably found yourself copy-pasting the same code blocks over and over. Database exports, plugin updates, backup routines—the same patterns repeat across different scripts.

This is where Bash functions become your secret weapon.

In this guide, you’ll learn how to write reusable, production-ready Bash functions for WordPress automation. We’ll cover parameter validation, error handling, logging, and real-world examples you can use immediately in your WP-CLI scripts.

Why Use Functions in Bash Scripts?

Functions are reusable blocks of code that make your scripts:

More Maintainable: Fix a bug once in the function, not in 10 different scripts.

Easier to Readbackup_wordpress_database is clearer than 20 lines of inline code.

More Testable: Test individual functions independently.

DRY (Don’t Repeat Yourself): Write once, use everywhere.

Professional: Production-ready scripts use functions extensively.

Before Functions: Repetitive Code

Here’s what many WordPress automation scripts look like without functions:

#!/bin/bash
# Script 1: Backup site A
cd /var/www/siteA
wp db export /backups/siteA-$(date +%Y%m%d).sql
tar -czf /backups/siteA-files-$(date +%Y%m%d).tar.gz .
echo "Backup completed for Site A"

# Script 2: Backup site B (almost identical!)
cd /var/www/siteB
wp db export /backups/siteB-$(date +%Y%m%d).sql
tar -czf /backups/siteB-files-$(date +%Y%m%d).tar.gz .
echo "Backup completed for Site B"

This violates DRY principles and becomes a maintenance nightmare.

After Functions: Clean and Reusable

With functions, the same logic becomes:

#!/bin/bash

# Reusable function
backup_wordpress() {
    local wp_path=$1
    local backup_dir=$2
    local site_name=$(basename "$wp_path")
    local timestamp=$(date +%Y%m%d)

    cd "$wp_path" || return 1
    wp db export "${backup_dir}/${site_name}-${timestamp}.sql" || return 1
    tar -czf "${backup_dir}/${site_name}-files-${timestamp}.tar.gz" . || return 1
    echo "Backup completed for ${site_name}"
}

# Use the function
backup_wordpress /var/www/siteA /backups
backup_wordpress /var/www/siteB /backups
backup_wordpress /var/www/siteC /backups

One function, multiple uses. Change the backup logic once, and all three sites benefit.

Basic Function Syntax {#basic-syntax}

Bash functions can be defined in two ways:

Method 1: Using the function Keyword

function my_function() {
    echo "Hello from my function"
}

Method 2: Without the function Keyword (POSIX-compliant)

my_function() {
    echo "Hello from my function"
}

Both work identically. Method 2 is more portable and preferred for production scripts.

Calling Functions

Simply use the function name:

my_function  # Calls the function

Example: Simple WordPress Function

#!/bin/bash

# Function to check WordPress version
check_wp_version() {
    wp core version
}

# Call it
check_wp_version

Output:

6.4.2

Parameter Handling in Functions

Functions accept parameters just like scripts. Parameters are accessed using $1$2$3, etc.

Basic Parameters

greet_user() {
    local name=$1
    echo "Hello, ${name}!"
}

greet_user "John"  # Output: Hello, John!

Multiple Parameters

update_wordpress() {
    local wp_path=$1
    local backup=$2

    cd "$wp_path" || return 1

    if [ "$backup" = "yes" ]; then
        wp db export backup.sql
    fi

    wp core update
}

# Usage
update_wordpress /var/www/html yes

Parameter Variables

Bash provides special variables for working with parameters:

  • $# – Number of parameters
  • $@ – All parameters as separate strings
  • $* – All parameters as single string
  • $1, $2, ... – Individual parameters

WordPress Example: Plugin Update Function

update_plugin() {
    local wp_path=$1
    local plugin_name=$2

    echo "Updating plugin: ${plugin_name} at ${wp_path}"
    wp plugin update "$plugin_name" --path="$wp_path"
}

# Usage
update_plugin /var/www/html woocommerce
update_plugin /var/www/html yoast-seo

Return Values and Exit Codes

Bash functions don’t return values like other programming languages. Instead, they use:

  1. Exit codes (0 for success, non-zero for failure)
  2. Echo statements (output that can be captured)

Exit Codes with return

check_wordpress_exists() {
    local wp_path=$1

    if [ -f "${wp_path}/wp-config.php" ]; then
        return 0  # Success
    else
        return 1  # Failure
    fi
}

# Usage
if check_wordpress_exists /var/www/html; then
    echo "WordPress found!"
else
    echo "WordPress not found"
fi

Best Practice: Always return 0 for success, 1+ for errors. This follows Unix conventions and works with if statements.

Returning Values via Echo

get_wordpress_version() {
    local wp_path=$1
    wp core version --path="$wp_path"
}

# Capture output
version=$(get_wordpress_version /var/www/html)
echo "WordPress version: ${version}"

Combining Both Approaches

backup_database() {
    local wp_path=$1
    local backup_file=$2

    # Perform backup
    if wp db export "$backup_file" --path="$wp_path" 2>/dev/null; then
        echo "$backup_file"  # Return filename
        return 0             # Success exit code
    else
        echo ""              # Return empty string
        return 1             # Failure exit code
    fi
}

# Usage
if backup_file=$(backup_database /var/www/html "/backups/db.sql"); then
    echo "Backup successful: ${backup_file}"
else
    echo "Backup failed"
fi

Error Handling Best Practices

Production-ready functions need robust error handling. Here’s how to do it right.

Use set -e Cautiously

#!/bin/bash
set -e  # Exit on any error

# This will exit the entire script if wp fails
wp plugin update --all

Problemset -e exits the entire script on first error, which might not be what you want in functions.

Better approach: Handle errors explicitly in functions.

Check Command Success

update_all_plugins() {
    local wp_path=$1

    if ! wp plugin update --all --path="$wp_path"; then
        echo "ERROR: Plugin update failed" >&2
        return 1
    fi

    echo "All plugins updated successfully"
    return 0
}

Validate Parameters

backup_wordpress() {
    local wp_path=$1
    local backup_dir=$2

    # Validate parameters exist
    if [ -z "$wp_path" ] || [ -z "$backup_dir" ]; then
        echo "ERROR: Missing parameters" >&2
        echo "Usage: backup_wordpress <wp_path> <backup_dir>" >&2
        return 1
    fi

    # Validate WordPress directory
    if [ ! -f "${wp_path}/wp-config.php" ]; then
        echo "ERROR: Not a WordPress installation: ${wp_path}" >&2
        return 1
    fi

    # Validate backup directory
    if [ ! -d "$backup_dir" ]; then
        echo "ERROR: Backup directory doesn't exist: ${backup_dir}" >&2
        return 1
    fi

    # Perform backup
    # ... backup logic here ...
}

Use Error Messages to STDERR

Send errors to stderr (file descriptor 2) instead of stdout:

log_error() {
    echo "[ERROR] $*" >&2
}

log_info() {
    echo "[INFO] $*"
}

# Usage
if ! wp core update; then
    log_error "WordPress core update failed"
    return 1
fi

log_info "WordPress updated successfully"

This allows users to separate errors from normal output:

./script.sh 2>errors.log  # Only errors go to file
./script.sh 2>&1 | grep ERROR  # Search errors

Real-World WordPress Functions {#wordpress-functions}

Let’s build production-ready functions you can use immediately.

Function 1: WordPress Site Backup

#!/bin/bash

backup_wordpress_site() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local site_name=$(basename "$wp_path")

    # Validate inputs
    if [ -z "$wp_path" ] || [ -z "$backup_dir" ]; then
        echo "ERROR: Usage: backup_wordpress_site <wp_path> <backup_dir>" >&2
        return 1
    fi

    if [ ! -f "${wp_path}/wp-config.php" ]; then
        echo "ERROR: Invalid WordPress path: ${wp_path}" >&2
        return 1
    fi

    # Create backup directory if needed
    mkdir -p "$backup_dir"

    # Backup database
    local db_file="${backup_dir}/${site_name}_db_${timestamp}.sql"
    echo "Backing up database..."
    if ! wp db export "$db_file" --path="$wp_path" --quiet; then
        echo "ERROR: Database backup failed" >&2
        return 1
    fi

    # Compress database
    gzip "$db_file"
    echo "Database backed up: ${db_file}.gz"

    # Backup files
    local files_archive="${backup_dir}/${site_name}_files_${timestamp}.tar.gz"
    echo "Backing up files..."
    if ! tar -czf "$files_archive" -C "$(dirname $wp_path)" "$(basename $wp_path)" 2>/dev/null; then
        echo "ERROR: File backup failed" >&2
        return 1
    fi

    echo "Files backed up: ${files_archive}"
    echo "Backup completed successfully"
    return 0
}

# Usage
backup_wordpress_site /var/www/html /backups

Function 2: Bulk Plugin Update with Verification

update_plugins_safe() {
    local wp_path=$1

    # Validation
    if [ ! -f "${wp_path}/wp-config.php" ]; then
        echo "ERROR: Invalid WordPress path" >&2
        return 1
    fi

    echo "Checking for plugin updates..."
    local updates=$(wp plugin list --update=available --path="$wp_path" --format=count)

    if [ "$updates" -eq 0 ]; then
        echo "All plugins are up to date"
        return 0
    fi

    echo "Found ${updates} plugin(s) to update"

    # Backup database before updates
    local backup_file="/tmp/pre-update-backup-$(date +%s).sql"
    echo "Creating safety backup..."
    if ! wp db export "$backup_file" --path="$wp_path" --quiet; then
        echo "ERROR: Safety backup failed, aborting" >&2
        return 1
    fi

    echo "Updating plugins..."
    if wp plugin update --all --path="$wp_path"; then
        echo "Plugins updated successfully"
        rm -f "$backup_file"  # Clean up backup if successful
        return 0
    else
        echo "ERROR: Plugin update failed" >&2
        echo "Safety backup available at: ${backup_file}"
        return 1
    fi
}

# Usage
update_plugins_safe /var/www/html

Function 3: WordPress Health Check

wordpress_health_check() {
    local wp_path=$1
    local errors=0

    echo "Running WordPress health check for: ${wp_path}"
    echo "=============================================="

    # Check 1: WordPress core
    echo -n "WordPress Core: "
    if wp core verify-checksums --path="$wp_path" &>/dev/null; then
        echo "✓ OK"
    else
        echo "✗ FAILED"
        ((errors++))
    fi

    # Check 2: Database
    echo -n "Database Connection: "
    if wp db check --path="$wp_path" &>/dev/null; then
        echo "✓ OK"
    else
        echo "✗ FAILED"
        ((errors++))
    fi

    # Check 3: Plugin updates
    echo -n "Plugin Updates: "
    local plugin_updates=$(wp plugin list --update=available --path="$wp_path" --format=count)
    if [ "$plugin_updates" -eq 0 ]; then
        echo "✓ All up to date"
    else
        echo "⚠ ${plugin_updates} update(s) available"
    fi

    # Check 4: Theme updates
    echo -n "Theme Updates: "
    local theme_updates=$(wp theme list --update=available --path="$wp_path" --format=count)
    if [ "$theme_updates" -eq 0 ]; then
        echo "✓ All up to date"
    else
        echo "⚠ ${theme_updates} update(s) available"
    fi

    # Check 5: Core version
    local wp_version=$(wp core version --path="$wp_path")
    echo "WordPress Version: ${wp_version}"

    echo "=============================================="
    if [ $errors -eq 0 ]; then
        echo "Health check passed"
        return 0
    else
        echo "Health check failed with ${errors} error(s)"
        return 1
    fi
}

# Usage
wordpress_health_check /var/www/html

Building a Function Library {#function-library}

Create a reusable library that you can source into any WordPress automation script.

Create the Library File

# Save as: ~/wp-functions.sh

#!/bin/bash
#
# WordPress Automation Function Library
# Source this file in your scripts: source ~/wp-functions.sh
#

# Color codes for output
RED='3[0;31m'
GREEN='3[0;32m'
YELLOW='3[1;33m'
NC='3[0m' # No Color

# Logging functions
log_info() {
    echo -e "${GREEN}[INFO]${NC} $*"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $*" >&2
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $*"
}

# Check if path is valid WordPress installation
is_wordpress() {
    local wp_path=$1
    [ -f "${wp_path}/wp-config.php" ]
}

# Get WordPress version
get_wp_version() {
    local wp_path=$1
    wp core version --path="$wp_path" 2>/dev/null
}

# Check if plugin is installed
has_plugin() {
    local wp_path=$1
    local plugin_name=$2
    wp plugin is-installed "$plugin_name" --path="$wp_path" 2>/dev/null
}

# Safely update WordPress core
safe_core_update() {
    local wp_path=$1

    if ! is_wordpress "$wp_path"; then
        log_error "Not a WordPress installation: ${wp_path}"
        return 1
    fi

    local current_version=$(get_wp_version "$wp_path")
    log_info "Current version: ${current_version}"

    # Backup first
    log_info "Creating backup..."
    local backup_file="/tmp/wp-backup-$(date +%s).sql"
    if ! wp db export "$backup_file" --path="$wp_path" --quiet; then
        log_error "Backup failed"
        return 1
    fi

    # Update
    log_info "Updating WordPress core..."
    if wp core update --path="$wp_path"; then
        local new_version=$(get_wp_version "$wp_path")
        log_info "Updated from ${current_version} to ${new_version}"
        rm -f "$backup_file"
        return 0
    else
        log_error "Update failed. Backup saved at: ${backup_file}"
        return 1
    fi
}

Using the Library

Create a script that sources the library:

#!/bin/bash
# my-script.sh

# Load function library
source ~/wp-functions.sh

# Use the functions
log_info "Starting WordPress maintenance..."

if is_wordpress /var/www/html; then
    log_info "WordPress found"
    safe_core_update /var/www/html
else
    log_error "WordPress not found"
    exit 1
fi

This approach keeps your scripts clean and maintainable. Update the library once, and all scripts benefit.

Advanced Patterns {#advanced-patterns}

Pattern 1: Functions with Named Parameters

backup_wordpress() {
    # Parse named parameters
    local wp_path=""
    local backup_dir=""
    local include_uploads=true

    while [[ $# -gt 0 ]]; do
        case $1 in
            --path)
                wp_path="$2"
                shift 2
                ;;
            --backup-dir)
                backup_dir="$2"
                shift 2
                ;;
            --no-uploads)
                include_uploads=false
                shift
                ;;
            *)
                echo "Unknown option: $1" >&2
                return 1
                ;;
        esac
    done

    # Use the parameters
    echo "Path: ${wp_path}"
    echo "Backup dir: ${backup_dir}"
    echo "Include uploads: ${include_uploads}"
}

# Usage
backup_wordpress --path /var/www/html --backup-dir /backups --no-uploads

Pattern 2: Functions with Default Parameters

update_wordpress() {
    local wp_path=${1:-/var/www/html}  # Default to /var/www/html
    local backup=${2:-yes}               # Default to yes

    echo "Path: ${wp_path}"
    echo "Backup: ${backup}"
}

# Call with defaults
update_wordpress

# Call with custom values
update_wordpress /var/www/mysite no

Pattern 3: Array Return Values

get_outdated_plugins() {
    local wp_path=$1
    wp plugin list --update=available --path="$wp_path" --field=name
}

# Capture into array
mapfile -t plugins < <(get_outdated_plugins /var/www/html)

# Loop through results
for plugin in "${plugins[@]}"; do
    echo "Outdated: ${plugin}"
done

Testing Your Functions {#testing}

Always test functions before using in production.

Manual Testing Script

#!/bin/bash
# test-functions.sh

source ~/wp-functions.sh

echo "Testing WordPress functions..."
echo "=============================="

# Test 1: is_wordpress
echo "Test 1: is_wordpress"
if is_wordpress /var/www/html; then
    echo "✓ PASS"
else
    echo "✗ FAIL"
fi

# Test 2: get_wp_version
echo "Test 2: get_wp_version"
version=$(get_wp_version /var/www/html)
if [ -n "$version" ]; then
    echo "✓ PASS: Version ${version}"
else
    echo "✗ FAIL"
fi

# Add more tests...

Automated Testing with BATS

For serious projects, use BATS (Bash Automated Testing System):

# Install BATS
git clone https://github.com/bats-core/bats-core.git
cd bats-core
./install.sh /usr/local

# Create test file: test-wp-functions.bats
#!/usr/bin/env bats

load ~/wp-functions.sh

@test "is_wordpress returns true for valid path" {
    run is_wordpress /var/www/html
    [ "$status" -eq 0 ]
}

@test "is_wordpress returns false for invalid path" {
    run is_wordpress /tmp
    [ "$status" -eq 1 ]
}

# Run tests
bats test-wp-functions.bats

Next Steps

You now know how to write reusable Bash functions for WordPress automation. Here’s what to learn next:

  1. Bulletproof Error Handling in Bash Scripts for WP-CLI – Take error handling to the next level
  2. How to Parse Command Line Arguments in WP-CLI Bash Scripts – Make your functions accept complex parameters
  3. Professional Logging and Debugging for WP-CLI Automation Scripts – Add production-grade logging

Practice Projects

Build a function library with these functions:

  • Database backup and restore
  • Plugin/theme management
  • User management
  • Content import/export
  • Security hardening

Create a WordPress maintenance script that:

  • Uses multiple functions from your library
  • Accepts command-line arguments
  • Has proper error handling
  • Outputs colored logs

Join WPCLI Mastery

Want to master WordPress automation with battle-tested patterns and real-world projects?

Join the WPCLI Mastery waitlist and get:

  • Free WordPress backup automation script (production-ready functions included)
  • Access to complete function library
  • Early bird course pricing ($99 vs $199)

Conclusion

Bash functions transform messy WordPress automation scripts into clean, maintainable, professional code. The patterns you learned here—parameter validation, error handling, return values—are the foundation of production-ready automation.

Key takeaways:

  • Functions eliminate code duplication
  • Always validate parameters
  • Use return codes (0 = success, 1+ = failure)
  • Handle errors explicitly
  • Build a reusable function library
  • Test your functions before production use

Start building your function library today. Every reusable function you write saves time on every future script.

Ready to build advanced WordPress automation? Check out our guide on automating WordPress backups with WP-CLI, which uses these function patterns in a complete project.


Questions about Bash functions for WordPress? Drop a comment below!

Found this helpful? Share it with other WordPress developers learning automation.

Leave a Reply

Your email address will not be published. Required fields are marked *