Batch Scanning and Deleting Empty WordPress Posts with WP-CLI

I get to do a lot of fun projects with my awesome clients on Codeable. This post was inspired by a client that wanted to delete a lot of WordPress posts with an empty title and content. I have no idea how they got there and neither did the site owner – it could have been a failed import of some kind or plugin that went nuts. Nevertheless they needed to be cleaned and this is a perfect job for my favorite WordPress tool: WP-CLI :). I have split the post into an audit and deletion section.

Auditing Empty WordPress Post Titles and Content Combinations

We are going to approach this methodically by doing some MySQL queries with WP-CLI’s wp db query command looking for empty post titles, content and even custom post types.

Empty Post Title

First we are going to find all of the posts with an empty title.

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_title=''" --allow-root

So many were output I decided to count them by piping the output to wc and counting the lines -l

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_title=''" --allow-root | wc -l

Almost 5000! Next we will check for just posts with empty content

4902

Empty Post Content

Now we can look for posts with empty content

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts where post_content=''" --allow-root

We can count the posts with empty content

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content=''" --allow-root | wc -l

Almost 5000 again! Let’s finally check to see if these are the same posts that also have an empty title

4902

Empty Post Title and Content

Finally let’s check the posts with both empty titles and content

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content='' AND post_title=''" --allow-root

Let us count those too

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content='' AND post_title=''" --allow-root | wc -l

Almost 5000, looks like a pattern 😉

4902

That confirms it! Now we can delete these with WP-CLI

Empty Post Title and Content for Custom Post Types

We can do the same thing for Custom Post Types by adding a post_type parameter, here we are looking for WooCommerce products so the post_type column has been set to product

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_type='product' AND post_content='' AND post_title=''" --allow-root

Let’s count these with wc and specifically the number of lines -l

wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_type='product' AND post_content='' AND post_title=''" --allow-root | wc -l

Now it is time to delete the empty posts!

Deleting Empty WordPress Post Titles and Content Combinations

Back up the database first before you actually delete anything!

wp db export before-empty-post-clean.sql --all-tablespaces --allow-root

Empty Post Title

This WP-CLI command deletes the posts with empty titles and their associated postmeta, the --force flag deletes them permanently by skipping the trash. The tr command is used to replace any new lines with spaces to keep the formatting that WP-CLI requires.

wp post delete $(wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_title=''" --allow-root | tr '\r\n' ' ') --force --allow-root

Empty Post Content

Here is a similar command but only targets the deletion of posts with empty post content

wp post delete $(wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content=''" --allow-root | tr '\r\n' ' ') --force --allow-root

Empty Post Title and Content

Finally this command will delete the posts that have an empty title and content

wp post delete $(wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content='' AND post_title=''" --allow-root | tr '\r\n' ' ') --force --allow-root

This will not delete associated postmeta but is faster since it is interacting with the database directly, could leave orphaned postmeta data leftover!

wp db query "DELETE FROM $(wp db prefix --allow-root)posts WHERE ID IN '($(wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_content='' AND post_title=''" --allow-root | tr '\r\n' ','))'"

Empty Custom Post Types

Last example is for deleting those empty WooCommerce products

wp post delete $(wp db query "SELECT ID FROM $(wp db prefix --allow-root)posts WHERE post_type='product' AND post_content=''" --allow-root | tr '\r\n' ' ') --force --allow-root

Sources

WP-CLI post delete command
Replacing newline with space using tr
Delete from MySQL using subquery as condition

1 thought on “Batch Scanning and Deleting Empty WordPress Posts with WP-CLI”

  1. after running the CLI commands and cleaning up THOUSANDS of posts w/ empty content/titles, i realized that the command doesn’t filter out by post type.

    i will say that *you did* touch upon this in the WooCommerce section. as this site did not have a WC instance, i skimmed over that section and moved on.

    BUT, between images, nav menu items, etc etc… doesn’t WP natively leave out some form of combination of title/content on several different post types?

    so, whereas i thought i was only cleaning out a few thousand posts, i ended up cleaning out (and i mean CLEANING OUT) a massive portion of the website’s content.

    i’m not trying to complain or anything, because this truly was my oversight. BUT, i would like to suggest that you modify your post to cover a bit more ground… so it can more accurately be applied to most WP instances.

    stumbling upon your website (via Kinsta) has been a true blessing! WP CLI is so very powerful… but with great power, comes great responsibility. i abused the power 😝

    ###
    more than likely, there aren’t all too many folks who would run into such a problem (as referred to within this post), or need a resolution for it. BUT, i was one of those guys!

    i have a MASSIVE blog that i’ve been growing for the last several years. it uses auto-generated content, pulling in from rss feeds & google images. needless to say, there have been several times when something was changed up on one of the several new post generation steps.

    when i ran the scripts to pull the counts, i was flabbergasted! WHOAH! TENS OF THOUSANDS of posts with empty content and several hundred posts with an empty title.

    well, one can only hope that all of those who attempted to clean them up (using the WP-CLI commands above) heeded your warning, AND MADE A COMPLETE BACKUP!

    i didn’t really give it much thought initially. and, it wasn’t until after I had reviewed the results that i noticed, but *then* it clicked.

Comments are closed.