Wes Ellis./ a personal notebook
Technology. Stories. Side projects.
A few things worth writing down.
← Back to Engineering

Engineering

WP-CLI: The Commands I Keep Coming Back To

A laptop on a desk showing lines of code in a dark editor, with warm orange light behind it.

Part 1 of the thread The WordPress toolbox

THE SHORT VERSION4 points
  • WP-CLI runs a WordPress site from a terminal. Anything you'd click through in wp-admin, you can script.
  • The everyday set is small: posts, plugins, users, options, and wp db export before anything risky.
  • A few commands are destructive by design. wp site empty --uploads isn't a health check, whatever my old notes said.
  • Pass --raw to wp config set for true/false values, and --parent takes a term ID, not a slug.

I kept a long WP-CLI cheat sheet in my notes for years. Going back through it against the current docs, most of it held up. A handful of lines were wrong, and two of them would have wrecked a site if I'd run them the way they were written. This is the trimmed-down version, with those fixed.

Checked against the WP-CLI docs in September 2026.

Getting it running

WP-CLI is a single PHP file. Download wp-cli.phar from the official builds repo, and check it with wp --info. It needs PHP 7.2.24 or newer.

On Windows there's no chmod +x, so the docs suggest a tiny batch file named wp.bat next to the phar, with the folder on your PATH:

@ECHO OFF
php "C:/wp-cli/wp-cli.phar" %*

Then cd into any WordPress folder and every command below works against that site. For a site on a remote host, you'd SSHSecure Shell. An encrypted way to get a command line on another machine over the network. in and run the same commands there.

The everyday set

Job Command
List drafts wp post list --post_status=draft --fields=ID,post_title
Create a post wp post create --post_title="Holiday hours" --post_status=draft
Update a post wp post update 123 --post_title="New title"
Plugins needing updates wp plugin list --update=available
Update everything wp plugin update --all and wp theme update --all
Add a user wp user create sam [email protected] --role=editor
Change the site title wp option update blogname "Example Bakery"
Back up the database wp db export --add-drop-table
Fix broken permalinks wp rewrite flush

The trick that makes WP-CLI feel like a real tool is --format=ids. It prints bare post IDs, which you can hand straight to another command:

wp post list --post_type=post --post_status=draft --format=ids
for id in $(wp post list --post_type=post --format=ids); do wp post term add "$id" category news; done
wp post delete $(wp post list --post_type=post --post_status=trash --format=ids)

Some commands, like wp post delete, take a whole list of IDs at once. Others, like wp post term add, want one post at a time, so they get a loop. That last line empties the trash, by the way: a post that's already in the trash is deleted for good.

wp post list passes most arguments through to WP_Query, so things like --meta_key and --meta_value work too.

The ones that bite

These are the lines I had to correct.

wp site empty --uploads was sitting in my notes under "site health check." It is not one. It deletes every post, comment and term on the site, and --uploads deletes the media files too. Users and settings survive. Nothing else does.

wp db query "UPDATE wp_posts SET post_status = 'draft' ..." with no WHERE on the ID quietly unpublishes every post you have. If you need raw SQL, export the database first and test on a copy.

wp post delete 123 sends the post to the trash. Add --force and it's gone for good, which is what my old bulk-delete line did to every draft at once.

wp config set WP_DEBUG true writes the string 'true' instead of the boolean. It happens to work for true, but wp config set WP_DEBUG false writes the string 'false', which PHP treats as on. Pass --raw for true, false and numbers.

wp term create category "Business" --parent=technology fails, because --parent wants the parent's term ID. Create the parent with --porcelain to get its ID back. And wp term create makes one term per run, so my line that tried to create four tags from one comma-separated string made a single tag with commas in its name.

Warning

Anything with --force, --all, reset or empty in it deserves a wp db export first. It takes seconds, and it's the difference between a bad afternoon and a bad week.

Quick health checks

These are all read-only, or close to it:

wp core verify-checksums
wp plugin verify-checksums --all
wp core check-update
wp transient delete --expired
wp cron event list

verify-checksums compares your files against the WordPress.org originals, so it's the fastest way to spot a hacked core file. The plugin version only knows about plugins hosted on WordPress.org, so premium plugins show up as "could not verify." That's expected.

Old notes also mentioned wp profile hook --spotlight for finding slow hooks. wp profile is a separate package (wp package install wp-cli/profile-command), not part of the standard install.

Tip

--dry-run exists on the scariest command of all, wp search-replace. I cover that one properly in looking after a WordPress database, since getting it wrong breaks serialized settings in ways that don't show up right away.

When a job has to happen from somewhere without shell access, like a scheduled task on a Windows box, the REST API covers a lot of the same ground over HTTPS. And before handing WP-CLI to anyone else, lock down the admin accounts it can create.