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 sitedraft- Not publishedpending- Awaiting reviewprivate- Visible only to logged-in userstrash- 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
| Task | WP-CLI | wp-admin |
|---|---|---|
| Create 100 Posts | ~30 seconds | 2-3 hours |
| Bulk Update Status | ~5 seconds | 10-15 minutes |
| Delete All Drafts | ~2 seconds | 5-10 minutes |
| Export Post List | Instant | Manual 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
- Manage taxonomies: Taxonomy Management
- Handle comments: Comment Management
- Learn database operations: Database Management