Skip to main content

Post Management

Overview

WP-CLI provides powerful commands to create, update, delete, and manage WordPress posts, pages, and custom post types. Perfect for content migrations, bulk operations, and automated workflows.

Understanding WordPress Posts

WordPress stores all content as "posts" in the database, including:

  • Posts - Blog articles
  • Pages - Static content
  • Custom Post Types - Products, courses, portfolios, etc.

WP-CLI's wp post commands give you complete control over all content types.

Creating Posts

Create Basic Post

# Create and publish a post
wp post create --post_title="Hello World" --post_status=publish

# Create draft post
wp post create --post_title="Draft Article" --post_status=draft

# Create with content
wp post create --post_title="My Post" --post_content="This is the content" --post_status=publish

Post statuses:

  • publish - Live on site
  • draft - Not published
  • pending - Awaiting review
  • private - Visible only to logged-in users
  • trash - In trash

Create with Full Options

# Create post with all options
wp post create \
--post_type=post \
--post_title="Complete Example" \
--post_content="Full article content here" \
--post_excerpt="Short summary" \
--post_author=2 \
--post_status=publish \
--post_category=5,12 \
--tags_input="wordpress,cli,tutorial" \
--comment_status=open \
--ping_status=closed

Create Pages

# Create page
wp post create --post_type=page --post_title="About Us" --post_status=publish

# Create page with parent
wp post create \
--post_type=page \
--post_title="Our Team" \
--post_parent=10 \
--post_status=publish

Create Custom Post Types

# Create WooCommerce product
wp post create \
--post_type=product \
--post_title="Premium Widget" \
--post_status=publish

# Create custom post type
wp post create \
--post_type=portfolio \
--post_title="Project Alpha" \
--post_status=publish

Get Post ID After Creation

# Get ID for use in scripts
POST_ID=$(wp post create --post_title="New Post" --post_status=publish --porcelain)
echo "Created post ID: $POST_ID"

# Use ID immediately
wp post meta add $POST_ID custom_field "custom value"

Listing Posts

Basic Listing

# List all posts
wp post list

# List with specific fields
wp post list --fields=ID,post_title,post_status,post_date

# List in table format
wp post list --format=table

Filter by Status

# List published posts
wp post list --post_status=publish

# List drafts
wp post list --post_status=draft

# List trashed posts
wp post list --post_status=trash

# List pending review
wp post list --post_status=pending

Filter by Type

# List pages
wp post list --post_type=page

# List products
wp post list --post_type=product

# List multiple types
wp post list --post_type=post,page

Advanced Filtering

# List posts by author
wp post list --post_author=2

# List posts from specific category
wp post list --post_category=5

# List posts with specific tag
wp post list --tag=wordpress

# List recent posts
wp post list --posts_per_page=10 --orderby=date --order=DESC

# Count posts
wp post list --post_status=publish --format=count

Export Post Lists

# Export to CSV
wp post list --post_status=publish --format=csv > published-posts.csv

# Export to JSON
wp post list --format=json > posts.json

# Export specific fields
wp post list --fields=ID,post_title,post_author --format=csv > post-report.csv

# Get just IDs
wp post list --post_status=draft --field=ID

Updating Posts

Update Single Post

# Update title
wp post update 123 --post_title="New Title"

# Update content
wp post update 123 --post_content="Updated content here"

# Update status
wp post update 123 --post_status=publish

# Update author
wp post update 123 --post_author=5

# Update multiple fields
wp post update 123 \
--post_title="Updated Title" \
--post_status=publish \
--post_author=2

Bulk Update Posts

# Publish all drafts
wp post update $(wp post list --post_status=draft --field=ID) --post_status=publish

# Change author for all posts
wp post update $(wp post list --post_author=1 --field=ID) --post_author=2

# Update category for all posts
wp post update $(wp post list --post_status=publish --field=ID) --post_category=10

Update with Confirmation

#!/bin/bash
# bulk-publish-drafts.sh

DRAFT_COUNT=$(wp post list --post_status=draft --format=count)

echo "Found $DRAFT_COUNT draft posts"
read -p "Publish all drafts? (y/n) " -n 1 -r
echo

if [[ $REPLY =~ ^[Yy]$ ]]; then
wp post update $(wp post list --post_status=draft --field=ID) --post_status=publish
echo "All drafts published!"
fi

Deleting Posts

Delete Single Post

# Move to trash
wp post delete 123

# Delete permanently (skip trash)
wp post delete 123 --force

# Delete multiple posts
wp post delete 123 456 789

Bulk Delete Operations

# Delete all drafts
wp post delete $(wp post list --post_status=draft --field=ID) --force

# Empty trash
wp post delete $(wp post list --post_status=trash --field=ID) --force

# Delete all posts by author
wp post delete $(wp post list --post_author=5 --field=ID) --force

Delete Revisions

# Count revisions
wp post list --post_type=revision --format=count

# Delete all revisions
wp post delete $(wp post list --post_type=revision --field=ID) --force

# Delete revisions for specific post
wp db query "DELETE FROM wp_posts WHERE post_parent=123 AND post_type='revision';"

Getting Post Information

Get Post Details

# Get all post data
wp post get 123

# Get specific field
wp post get 123 --field=post_title
wp post get 123 --field=post_content
wp post get 123 --field=post_status
wp post get 123 --field=post_author

# Get in JSON format
wp post get 123 --format=json

Check Post Existence

# Check if post exists
if wp post get 123 > /dev/null 2>&1; then
echo "Post exists"
else
echo "Post not found"
fi

Real-World Scenarios

Scenario 1: Content Migration

#!/bin/bash
# migrate-posts.sh - Migrate posts from CSV

# CSV format: title,content,status,author
while IFS=, read -r title content status author; do
echo "Creating: $title"
wp post create \
--post_title="$title" \
--post_content="$content" \
--post_status="$status" \
--post_author="$author"
done < posts.csv

echo "Migration complete!"

Scenario 2: Generate Test Content

#!/bin/bash
# generate-test-posts.sh

for i in {1..50}; do
wp post create \
--post_title="Test Post $i" \
--post_content="This is test content for post number $i" \
--post_status=publish \
--post_author=1
done

echo "Created 50 test posts"

Scenario 3: Content Audit

#!/bin/bash
# content-audit.sh

echo "=== Content Audit Report ==="
echo "Date: $(date)"
echo ""

echo "Total Posts: $(wp post list --post_type=post --format=count)"
echo "Published: $(wp post list --post_type=post --post_status=publish --format=count)"
echo "Drafts: $(wp post list --post_type=post --post_status=draft --format=count)"
echo "Pending: $(wp post list --post_type=post --post_status=pending --format=count)"
echo "Trash: $(wp post list --post_type=post --post_status=trash --format=count)"
echo ""

echo "Total Pages: $(wp post list --post_type=page --format=count)"
echo ""

echo "Posts by Author:"
wp db query "
SELECT
u.display_name,
COUNT(*) as post_count
FROM wp_posts p
JOIN wp_users u ON p.post_author = u.ID
WHERE p.post_type = 'post' AND p.post_status = 'publish'
GROUP BY p.post_author
ORDER BY post_count DESC;
"

Scenario 4: Cleanup Old Content

#!/bin/bash
# cleanup-old-posts.sh

# Delete posts older than 2 years
CUTOFF_DATE=$(date -d "2 years ago" +%Y-%m-%d)

echo "Deleting posts older than $CUTOFF_DATE..."

OLD_POSTS=$(wp post list \
--post_type=post \
--post_status=publish \
--before="$CUTOFF_DATE" \
--field=ID)

if [ -n "$OLD_POSTS" ]; then
echo "Found $(echo $OLD_POSTS | wc -w) old posts"
read -p "Delete these posts? (y/n) " -n 1 -r
echo

if [[ $REPLY =~ ^[Yy]$ ]]; then
wp post delete $OLD_POSTS --force
echo "Old posts deleted"
fi
else
echo "No old posts found"
fi

Scenario 5: Bulk Author Reassignment

#!/bin/bash
# reassign-author.sh

OLD_AUTHOR=1
NEW_AUTHOR=2

POST_COUNT=$(wp post list --post_author=$OLD_AUTHOR --format=count)

echo "Found $POST_COUNT posts by author $OLD_AUTHOR"
read -p "Reassign to author $NEW_AUTHOR? (y/n) " -n 1 -r
echo

if [[ $REPLY =~ ^[Yy]$ ]]; then
wp post update \
$(wp post list --post_author=$OLD_AUTHOR --field=ID) \
--post_author=$NEW_AUTHOR
echo "Posts reassigned!"
fi

Post Meta Management

Add Post Meta

# Add custom field
wp post meta add 123 custom_field "custom value"

# Add multiple meta fields
wp post meta add 123 price "29.99"
wp post meta add 123 sku "PROD-001"

Get Post Meta

# Get specific meta
wp post meta get 123 custom_field

# List all meta for post
wp post meta list 123

# Get meta in JSON
wp post meta list 123 --format=json

Update Post Meta

# Update meta value
wp post meta update 123 price "39.99"

# Update or add if doesn't exist
wp post meta update 123 featured "yes"

Delete Post Meta

# Delete specific meta
wp post meta delete 123 old_field

# Delete all meta for a key
wp post meta delete 123 temporary_data

Advanced Operations

Generate Posts from Template

#!/bin/bash
# generate-from-template.sh

TEMPLATE="This is a template post with {{TITLE}} and {{NUMBER}}"

for i in {1..10}; do
CONTENT=$(echo "$TEMPLATE" | sed "s/{{TITLE}}/Post $i/g" | sed "s/{{NUMBER}}/$i/g")

wp post create \
--post_title="Generated Post $i" \
--post_content="$CONTENT" \
--post_status=publish
done

Clone Posts

#!/bin/bash
# clone-post.sh

SOURCE_ID=$1
NUM_COPIES=$2

# Get source post data
TITLE=$(wp post get $SOURCE_ID --field=post_title)
CONTENT=$(wp post get $SOURCE_ID --field=post_content)

for i in $(seq 1 $NUM_COPIES); do
wp post create \
--post_title="$TITLE (Copy $i)" \
--post_content="$CONTENT" \
--post_status=draft
done

echo "Created $NUM_COPIES copies of post $SOURCE_ID"

Schedule Posts

# Create post scheduled for future
FUTURE_DATE=$(date -d "+7 days" +"%Y-%m-%d %H:%M:%S")

wp post create \
--post_title="Scheduled Post" \
--post_content="This will be published in 7 days" \
--post_status=future \
--post_date="$FUTURE_DATE"

Best Practices

1. Always Backup Before Bulk Operations

# Backup database before bulk changes
wp db export backup-before-bulk-$(date +%Y%m%d).sql

2. Use Dry Run for Testing

# Test what would be deleted
wp post list --post_status=draft --field=ID

# Then actually delete
wp post delete $(wp post list --post_status=draft --field=ID) --force

3. Use Porcelain for Scripts

# Get just the ID for scripting
POST_ID=$(wp post create --post_title="New Post" --porcelain)
echo "Created post: $POST_ID"

4. Filter Carefully

# Be specific with filters to avoid mistakes
wp post delete $(wp post list --post_type=post --post_status=draft --post_author=5 --field=ID) --force

5. Log Bulk Operations

#!/bin/bash
# bulk-operation-with-logging.sh

LOG_FILE="bulk-operations-$(date +%Y%m%d).log"

echo "Starting bulk operation at $(date)" >> $LOG_FILE

wp post update $(wp post list --post_status=draft --field=ID) --post_status=publish 2>&1 | tee -a $LOG_FILE

echo "Completed at $(date)" >> $LOG_FILE

Quick Reference

Essential Commands

# Create
wp post create --post_title="Title" --post_status=publish
wp post create --post_type=page --post_title="Page Title"
wp post create --porcelain # Return only ID

# List
wp post list # All posts
wp post list --post_status=publish # Published only
wp post list --post_type=page # Pages only
wp post list --post_author=2 # By author
wp post list --format=count # Count only
wp post list --field=ID # IDs only

# Update
wp post update 123 --post_title="New Title"
wp post update 123 --post_status=publish
wp post update $(wp post list --post_status=draft --field=ID) --post_status=publish

# Delete
wp post delete 123 # Move to trash
wp post delete 123 --force # Permanent delete
wp post delete $(wp post list --post_status=trash --field=ID) --force

# Get
wp post get 123 # All data
wp post get 123 --field=post_title # Specific field

# Meta
wp post meta add 123 key "value"
wp post meta get 123 key
wp post meta update 123 key "new value"
wp post meta delete 123 key
wp post meta list 123

Comparison: WP-CLI vs wp-admin

TaskWP-CLIwp-admin
Create 100 Posts~30 seconds2-3 hours
Bulk Update Status~5 seconds10-15 minutes
Delete All Drafts~2 seconds5-10 minutes
Export Post ListInstantManual copy/paste
Automation✅ Fully scriptable❌ Manual only
Visual Editor❌ Command-line✅ WYSIWYG
Media Upload⚠️ Separate command✅ Integrated
Key Takeaway

WP-CLI excels at bulk operations, automation, and migrations. Use it for large-scale content management. Use wp-admin for individual post editing with visual feedback.

Next Steps