Advanced Search & Replace Techniques
Overview
Beyond basic search-replace operations, WP-CLI offers advanced techniques for complex scenarios: regex-like patterns, conditional replacements, data transformation, and specialized migration workflows.
Complex Migration Scenarios
Multi-Domain Migrations
#!/bin/bash
# multi-domain-migration.sh - Handle multiple domain replacements
# Backup first
wp db export pre-multi-domain-migration.sql
# Replace multiple domain variations
declare -A domains=(
["http://oldsite.com"]="https://newsite.com"
["http://www.oldsite.com"]="https://www.newsite.com"
["https://oldsite.com"]="https://newsite.com"
["https://www.oldsite.com"]="https://www.newsite.com"
)
for old in "${!domains[@]}"; do
new="${domains[$old]}"
echo "Replacing: $old → $new"
wp search-replace "$old" "$new" --precise --skip-columns=guid --dry-run
done
read -p "Apply all replacements? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
for old in "${!domains[@]}"; do
new="${domains[$old]}"
wp search-replace "$old" "$new" --precise --skip-columns=guid
done
echo "All domain replacements complete!"
fi
Subdirectory to Root Migration
#!/bin/bash
# subdirectory-to-root.sh
# Moving from example.com/blog to example.com
OLD_URL="https://example.com/blog"
NEW_URL="https://example.com"
echo "Migrating from subdirectory to root..."
# Backup
wp db export backup-subdir-to-root.sql
# Replace URLs
wp search-replace "$OLD_URL" "$NEW_URL" --precise --skip-columns=guid --dry-run
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
wp search-replace "$OLD_URL" "$NEW_URL" --precise --skip-columns=guid
# Update WordPress options
wp option update home "$NEW_URL"
wp option update siteurl "$NEW_URL"
# Flush permalinks
wp rewrite flush
echo "Migration complete! Update your .htaccess and web server config."
fi
Root to Subdirectory Migration
#!/bin/bash
# root-to-subdirectory.sh
# Moving from example.com to example.com/blog
OLD_URL="https://example.com"
NEW_URL="https://example.com/blog"
echo "Migrating from root to subdirectory..."
# Backup
wp db export backup-root-to-subdir.sql
# Replace URLs (be careful with trailing slashes)
wp search-replace "$OLD_URL" "$NEW_URL" --precise --skip-columns=guid --dry-run
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
wp search-replace "$OLD_URL" "$NEW_URL" --precise --skip-columns=guid
# Update options
wp option update home "$NEW_URL"
wp option update siteurl "$NEW_URL"
# Flush permalinks
wp rewrite flush
echo "Migration complete! Move WordPress files to /blog directory."
fi
Selective Data Replacement
Replace Only in Published Posts
# Get published post IDs
POST_IDS=$(wp post list --post_status=publish --field=ID --format=csv)
# Replace only in those posts
wp db query "
UPDATE wp_posts
SET post_content = REPLACE(post_content, 'old-text', 'new-text')
WHERE ID IN ($POST_IDS);
"
# Note: This doesn't handle serialized data
# For serialized data, use search-replace on specific table
wp search-replace 'old-text' 'new-text' wp_posts --precise
Replace in Specific Post Types
#!/bin/bash
# replace-in-post-type.sh
POST_TYPE="product" # WooCommerce products
OLD_TEXT="old-brand"
NEW_TEXT="new-brand"
echo "Replacing in $POST_TYPE post type..."
# Get count of affected posts
COUNT=$(wp db query "
SELECT COUNT(*)
FROM wp_posts
WHERE post_type='$POST_TYPE'
AND post_content LIKE '%$OLD_TEXT%';
" --skip-column-names)
echo "Found $COUNT posts with '$OLD_TEXT'"
# Perform replacement
wp db query "
UPDATE wp_posts
SET post_content = REPLACE(post_content, '$OLD_TEXT', '$NEW_TEXT')
WHERE post_type='$POST_TYPE';
"
echo "Replacement complete!"
Replace Only in Specific Date Range
#!/bin/bash
# replace-in-date-range.sh
START_DATE="2024-01-01"
END_DATE="2024-12-31"
OLD_TEXT="old-text"
NEW_TEXT="new-text"
echo "Replacing content in posts from $START_DATE to $END_DATE..."
wp db query "
UPDATE wp_posts
SET post_content = REPLACE(post_content, '$OLD_TEXT', '$NEW_TEXT')
WHERE post_date >= '$START_DATE'
AND post_date <= '$END_DATE'
AND post_status = 'publish';
"
echo "Replacement complete!"
Media and Upload Path Migrations
Update Upload Paths
#!/bin/bash
# update-upload-paths.sh
OLD_PATH="/wp-content/uploads"
NEW_PATH="/content/uploads"
echo "Updating upload paths..."
# Dry run
wp search-replace "$OLD_PATH" "$NEW_PATH" --dry-run
# Apply
wp search-replace "$OLD_PATH" "$NEW_PATH" --precise
# Update upload_path option if set
wp option update upload_path "$NEW_PATH"
echo "Upload paths updated!"
CDN URL Replacement
#!/bin/bash
# cdn-migration.sh
SITE_URL="https://example.com"
CDN_URL="https://cdn.example.com"
echo "Migrating media to CDN..."
# Replace image URLs
wp search-replace "$SITE_URL/wp-content/uploads" "$CDN_URL/wp-content/uploads" --dry-run
read -p "Apply CDN URLs? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
wp search-replace "$SITE_URL/wp-content/uploads" "$CDN_URL/wp-content/uploads" --precise
echo "CDN migration complete!"
fi
Reverse CDN Migration
#!/bin/bash
# reverse-cdn-migration.sh
CDN_URL="https://cdn.example.com"
SITE_URL="https://example.com"
echo "Removing CDN URLs..."
wp search-replace "$CDN_URL/wp-content/uploads" "$SITE_URL/wp-content/uploads" --precise
echo "CDN URLs removed!"
Multisite Advanced Operations
Replace on Specific Subsites
#!/bin/bash
# multisite-selective-replace.sh
# Array of subsite URLs to update
SUBSITES=(
"site1.example.com"
"site2.example.com"
"site3.example.com"
)
OLD_TEXT="old-company"
NEW_TEXT="new-company"
for site in "${SUBSITES[@]}"; do
echo "Updating $site..."
wp search-replace "$OLD_TEXT" "$NEW_TEXT" --url=$site --precise
done
echo "All subsites updated!"
Network-Wide with Exclusions
#!/bin/bash
# network-replace-with-exclusions.sh
OLD_TEXT="old-text"
NEW_TEXT="new-text"
EXCLUDE_SITES=("archive.example.com" "old.example.com")
# Get all site URLs
ALL_SITES=$(wp site list --field=url)
for site in $ALL_SITES; do
# Check if site should be excluded
if [[ " ${EXCLUDE_SITES[@]} " =~ " ${site} " ]]; then
echo "Skipping $site (excluded)"
continue
fi
echo "Updating $site..."
wp search-replace "$OLD_TEXT" "$NEW_TEXT" --url=$site --precise
done
echo "Network update complete!"
Data Transformation
Normalize URLs (Remove Trailing Slashes)
#!/bin/bash
# normalize-urls.sh
echo "Normalizing URLs (removing trailing slashes)..."
# This requires custom SQL as search-replace can't handle patterns
wp db query "
UPDATE wp_posts
SET post_content = REGEXP_REPLACE(
post_content,
'(https?://[^\"\\s]+)/',
'\\1'
)
WHERE post_content REGEXP '(https?://[^\"\\s]+)/';
"
echo "URLs normalized!"
Convert HTTP to HTTPS (Comprehensive)
#!/bin/bash
# comprehensive-https-migration.sh
DOMAIN="example.com"
echo "Converting entire site to HTTPS..."
# Backup
wp db export backup-before-https.sql
# Replace all HTTP instances
wp search-replace "http://$DOMAIN" "https://$DOMAIN" --precise --skip-columns=guid --dry-run
read -p "Apply HTTPS conversion? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Main replacement
wp search-replace "http://$DOMAIN" "https://$DOMAIN" --precise --skip-columns=guid
# Update options
wp option update home "https://$DOMAIN"
wp option update siteurl "https://$DOMAIN"
# Update any remaining http:// in options
wp search-replace "http://" "https://" wp_options --precise
# Clear caches
wp cache flush
wp rewrite flush
echo "HTTPS conversion complete!"
echo "Don't forget to:"
echo "1. Update your SSL certificate"
echo "2. Update .htaccess to force HTTPS"
echo "3. Test all functionality"
fi
Clean Up Shortcodes
#!/bin/bash
# clean-shortcodes.sh
# Remove specific shortcode
SHORTCODE="old_gallery"
echo "Removing [$SHORTCODE] shortcodes..."
# Simple shortcode
wp search-replace "[$SHORTCODE]" "" --include-columns=post_content
# Shortcode with any attributes (requires SQL)
wp db query "
UPDATE wp_posts
SET post_content = REGEXP_REPLACE(
post_content,
'\\[$SHORTCODE[^\\]]*\\]',
''
)
WHERE post_content REGEXP '\\[$SHORTCODE';
"
echo "Shortcodes removed!"
Performance Optimization
Large Database Replacements
#!/bin/bash
# large-db-replacement.sh
OLD_TEXT="old-text"
NEW_TEXT="new-text"
echo "Performing large database replacement..."
# For very large databases, process table by table
TABLES=$(wp db tables --format=csv)
for table in ${TABLES//,/ }; do
echo "Processing $table..."
wp search-replace "$OLD_TEXT" "$NEW_TEXT" "$table" --precise
done
echo "Large database replacement complete!"
Batch Processing
#!/bin/bash
# batch-process-replacements.sh
# Process replacements in batches to avoid memory issues
BATCH_SIZE=1000
OLD_TEXT="old"
NEW_TEXT="new"
# Get total post count
TOTAL=$(wp post list --post_type=post --format=count)
BATCHES=$(( ($TOTAL + $BATCH_SIZE - 1) / $BATCH_SIZE ))
echo "Processing $TOTAL posts in $BATCHES batches..."
for ((i=0; i<$BATCHES; i++)); do
OFFSET=$(($i * $BATCH_SIZE))
echo "Batch $((i+1))/$BATCHES (offset: $OFFSET)..."
# Get post IDs for this batch
POST_IDS=$(wp post list --post_type=post --posts_per_page=$BATCH_SIZE --offset=$OFFSET --field=ID --format=csv)
# Process this batch
wp db query "
UPDATE wp_posts
SET post_content = REPLACE(post_content, '$OLD_TEXT', '$NEW_TEXT')
WHERE ID IN ($POST_IDS);
"
done
echo "Batch processing complete!"
Safety and Verification
Pre-Flight Checks
#!/bin/bash
# preflight-checks.sh
OLD_URL="http://oldsite.com"
NEW_URL="https://newsite.com"
echo "=== Pre-Flight Checks ==="
# 1. Check if old URL exists
COUNT=$(wp db query "
SELECT COUNT(*)
FROM wp_posts
WHERE post_content LIKE '%$OLD_URL%';
" --skip-column-names)
echo "Found $COUNT posts containing old URL"
# 2. Check database size
SIZE=$(wp db query "
SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.TABLES
WHERE table_schema = DATABASE();
" --skip-column-names)
echo "Database size: ${SIZE}MB"
# 3. Check disk space
AVAILABLE=$(df -h . | awk 'NR==2 {print $4}')
echo "Available disk space: $AVAILABLE"
# 4. Verify backup exists
if [ -f "backup.sql" ]; then
echo "✓ Backup file exists"
else
echo "✗ No backup found - creating one..."
wp db export backup.sql
fi
# 5. Dry run
echo ""
echo "Dry run results:"
wp search-replace "$OLD_URL" "$NEW_URL" --dry-run --precise
echo ""
echo "=== Pre-Flight Checks Complete ==="
Post-Migration Verification
#!/bin/bash
# post-migration-verification.sh
NEW_URL="https://newsite.com"
echo "=== Post-Migration Verification ==="
# 1. Check home and siteurl
HOME=$(wp option get home)
SITEURL=$(wp option get siteurl)
echo "Home URL: $HOME"
echo "Site URL: $SITEURL"
if [ "$HOME" == "$NEW_URL" ] && [ "$SITEURL" == "$NEW_URL" ]; then
echo "✓ URLs correctly updated"
else
echo "✗ URL mismatch detected!"
fi
# 2. Check for remaining old URLs
OLD_URL_COUNT=$(wp db query "
SELECT COUNT(*)
FROM wp_posts
WHERE post_content LIKE '%http://oldsite.com%';
" --skip-column-names)
if [ "$OLD_URL_COUNT" -eq 0 ]; then
echo "✓ No old URLs found"
else
echo "⚠ Warning: $OLD_URL_COUNT instances of old URL still exist"
fi
# 3. Check database integrity
wp db check > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✓ Database integrity OK"
else
echo "✗ Database integrity issues detected"
fi
# 4. Test site accessibility
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$NEW_URL")
if [ "$HTTP_CODE" == "200" ]; then
echo "✓ Site accessible (HTTP $HTTP_CODE)"
else
echo "✗ Site not accessible (HTTP $HTTP_CODE)"
fi
echo ""
echo "=== Verification Complete ==="
Quick Reference
Advanced Commands
# Multi-domain replacement
for old in old1.com old2.com; do
wp search-replace "$old" "new.com" --precise
done
# Table-by-table processing
for table in $(wp db tables); do
wp search-replace 'old' 'new' "$table"
done
# Conditional replacement (SQL)
wp db query "UPDATE wp_posts SET post_content = REPLACE(post_content, 'old', 'new') WHERE post_type='page';"
# Export changes as SQL
wp search-replace 'old' 'new' --export=changes.sql
# Network with exclusions
wp site list --field=url | grep -v "exclude.com" | xargs -I {} wp search-replace 'old' 'new' --url={}
Summary
| Technique | Use Case | Complexity |
|---|---|---|
| Multi-Domain | Complex migrations | High |
| Selective Replacement | Specific post types/dates | Medium |
| Media Migrations | CDN, upload path changes | Medium |
| Multisite Operations | Network-wide updates | High |
| Data Transformation | URL normalization, cleanup | High |
| Batch Processing | Large databases | High |
| Verification | Ensure migration success | Medium |
Key Takeaway
Advanced search-replace techniques enable complex migrations, selective updates, and large-scale transformations. Always use dry-run, backup first, and verify results.
Next Steps
- Optimize your database: Database Optimization
- Learn basic operations: Database Operations
- Master user management: User Management