Have you ever wanted to generate hundreds of user accounts or publish thousands of posts to your blog without ever once logging into your WordPress dashboard? You can do that and so much more with WordPress Command Line Interface (WP-CLI).

What is a command-line interface (CLI)?
A CLI, put simply, is a way to interact with a computer program using commands in the form of successive lines of text instead of using a graphical interface. For example, to install the plugin Hello Dolly instead of going to your WP Dashboard > Plugins > Add New > Search “Hello Dolly” > Click Install > and finally Click Activate, you can simply run the command wp plugin install hello-dolly --activate and it does it all at once.

Our First Command
Now that we know what a WP-CLI command is, let’s get started with our first Hello World! To create your first command, use the command WP_CLI::add_command('IDENTIFIER', 'CLASS_NAME') where the identifier is what you’ll use in the CLI to call subcommands and class name being the class containing all your CLI commands.
With our CLI registered, any public function declared in our class will be made accessible as a subcommand under our identifier. For the time being, we’ll simply use hello_world() and return the universal string to make sure our commands are registering. Finally, if we run wp wds hello_world we should have ‘Hello World’ returned.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| Plugin Name: WDS Hello World | |
| Plugin URI: https://webdevstudios.com/ | |
| Description: Teaching the basics of WP-CLI | |
| Author: Web Dev Studios | |
| Version: 1.0.0 | |
| Author URI: https://webdevstudios.com/ | |
| */ | |
| class WDS_CLI { | |
| /** | |
| * Returns 'Hello World' | |
| * | |
| * @since 0.0.1 | |
| * @author Scott Anderson | |
| */ | |
| public function hello_world() { | |
| WP_CLI::line( 'Hello World!' ); | |
| } | |
| } | |
| /** | |
| * Registers our command when cli get's initialized. | |
| * | |
| * @since 1.0.0 | |
| * @author Scott Anderson | |
| */ | |
| function wds_cli_register_commands() { | |
| WP_CLI::add_command( 'wds', 'WDS_CLI' ); | |
| } | |
| add_action( 'cli_init', 'wds_cli_register_commands' ); |
If we run the command wp wds --help we can see the subcommand we just registered.

Adding Arguments
Now it’s time to spice things up. More than likely, you’ll want to pass variables to your CLI command; when in CLI, these are called arguments. In order for your command to support arguments, add the variables $args and $assoc_args to the function declaration. The $args array will contain all positional arguments (arguments that don’t have a flag on it — ). The $assoc_args contain all arguments declared with flags –key=value –option or –no-flag. The –key=value will use the key to access the value assigned to it $assoc_args[‘key’] . The –option will return true, if its flag is present $assoc_args[‘option’], and –no-flag will return the false for a given flag used in the key $assoc_args[‘flag’] . Let’s see all these arguments in practice by running wp wds display_arguments John Doe 'Jane Doe' 32 --title='Moby Dick' --author='Herman Melville' --published=1851 --publish --no-archive.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Returns all arguments passed into command to demonstrate how to access them. | |
| * | |
| * @param Array $args Arguments in array format. | |
| * @param Array $assoc_args Key value arguments stored in associated array format. | |
| * @since 1.0.0 | |
| * @author Scott Anderson | |
| */ | |
| public function display_arguments( $args, $assoc_args ) { | |
| // Run command wp wds display_arguments John Doe 'Jane Doe' 32 –title='Moby Dick' –author='Herman Melville' –published=1851 –publish –no-archive | |
| // Examples of Arguments. | |
| WP_CLI::line( var_export($args[0]) ); // John | |
| WP_CLI::line( var_export($args[1]) ); // Doe | |
| WP_CLI::line( var_export($args[2]) ); // Jane Doe | |
| WP_CLI::line( var_export($args[3]) ); // 32 | |
| // Example of Associated Arguments | |
| WP_CLI::line( var_export($assoc_args['title']) ); // Moby Dick | |
| WP_CLI::line( var_export($assoc_args['author']) ); // Herman Melville | |
| WP_CLI::line( var_export($assoc_args['published']) ); // 1851 | |
| // Example of Associated Arguments as flag | |
| WP_CLI::line( var_export($assoc_args['publish']) ); // True | |
| WP_CLI::line( var_export($assoc_args['archive']) ); // False | |
| } |
Spicing Up Our Output
Up to now, we have only used WP_CLI::line to return data to the command line, which can be rather boring. For more message offerings in addition to line, we can use log, success, warning, error, error_multi_line, halt. When the flag –quiet is used, all messages, except those noted, will be silenced and not return a message. Let’s see these message types in practice by running wp wds display_messages.
Note: Colorize has multiple colors and output formats. For a full list, see the Colorize Codex.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Returns multiple messages to demonstrate different command return types. | |
| * | |
| * @since 1.0.0 | |
| * @author Scott Anderson | |
| */ | |
| public function display_messages( $args, $assoc_args ) { | |
| // No prepends. | |
| WP_CLI::line( 'Standard line return.' ); // No prefix on line return. | |
| WP_CLI::log( 'Standard line returned that wont be silenced.' ); // No prefix on line but ignores –quiet | |
| // Color make sure to use %n at the end of a style or else the style will apply to the next output. | |
| WP_CLI::line(WP_CLI::colorize( '%BBlue text%n' )); // Returns text in blue color. Ignores –quiet. | |
| WP_CLI::line(WP_CLI::colorize( '%MMagenta text%n' )); // Returns text in magenta. Ignores –quiet. | |
| WP_CLI::line(WP_CLI::colorize( '%UUnderline text%n' )); // Returns text underlined. Ignores –quiet. | |
| // Only prepends. | |
| WP_CLI::success( 'Post updated!' ); // Prepends Success to message | |
| WP_CLI::warning( 'No match was found.' ); // Prepends Warning to message. | |
| // Special conditions. | |
| WP_CLI::debug( 'Breakpoint comment.' ); // Displays only when –debug flag is used. | |
| WP_CLI::error_multi_line( ['Error found!', 'Post not updated!','User not updated!'] ); // Displays multi-line error in red box. Doesn't exit script. Ignores –quiet but looses formating. | |
| //Returns error message if –error custom flag is added. | |
| if( isset($assoc_args['error']) && $assoc_args['error']) { | |
| WP_CLI::error( 'Error found!' ); // Prepends message with Error and exits script. | |
| } | |
| WP_CLI::halt( 200 ); // Halts script execution with a specific return code. Could for calling your subcommand from another. | |
| } |
Loading, Loading, Loading…
When you are making a lot of operations, instead of leaving the user guessing how far along a process might be, it would be helpful to provide a progress bar keeping track of changes. Luckily WP-CLI has a progress bar baked right in. To use the progress bar, simply declare it $progress = WP_CLIUtilsmake_progress_bar( $label, $count ) where $label is a message shown before the progress bar and $count is the total number of ticks that should be performed.
With your progress bar initialized, it is most common to use it in a conditional loop. Once a stage of your process is complete, you can call the $progress->tick() command to increment the progress bar. Finally, at the end of your loop, you may call $progress->finish() to complete the progress bar. Let’s see the progress bar in practice by running wp wds generate_posts_progress_bar 100.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Displays progress bar to demonstrate progression through a time consuming process. | |
| * | |
| * @param Array $args Arguments in array format. | |
| * @param Array $assoc_args Key value arguments stored in associated array format. | |
| * @since 1.0.0 | |
| * @author Scott Anderson | |
| */ | |
| public function generate_posts_progress_bar( $args, $assoc_args ) { | |
| $desired_posts_to_generate = (int) $args[0]; | |
| $progress = \WP_CLI\Utils\make_progress_bar( 'Generating Posts', $desired_posts_to_generate ); | |
| for ( $i = 0; $i < $desired_posts_to_generate; $i++ ) { | |
| // Code used to generate a post. | |
| $progress->tick(); | |
| } | |
| $progress->finish(); | |
| } |
Putting It All Together
Up to this point, we have covered how to register commands, pass arguments, display information, and keep track of a task with a progress bar. Let’s round up with one final working example putting all of these elements together in one final command.
The following will generate posts where the first argument is how many posts to generate, second argument is title to be used in the posts generated, third argument is the ID of the author to associate it to, finally, any –key=value pairs will be stored as metadata for the post. We’ll wrap this with a progress bar to finish it up. We can generate 1000 posts with dummy metadata running the following command wp wds generate_posts 1000 'Testing Titles' 1 --locale="en_ca" --location="Toronto".


This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Generate posts with meta values. | |
| * | |
| * @param Array $args Arguments in array format. | |
| * @param Array $assoc_args Key value arguments stored in associated array format. | |
| * @since 1.0.0 | |
| * @author Scott Anderson | |
| */ | |
| public function generate_posts( $args, $assoc_args ) { | |
| // Get Post Details. | |
| $desired_posts_to_generate = (int) $args[0]; // First argument is how many posts should be generated. | |
| $title_prepend = $args[1]; // Second argument should be the title of posts generated. This will be used with index in loop to generate a title. | |
| $author_id = (int) $args[2]; // Id of author who to assign generated post to. | |
| $progress = \WP_CLI\Utils\make_progress_bar( 'Generating Posts', $desired_posts_to_generate ); | |
| for ( $i = 0; $i < $desired_posts_to_generate; $i++ ) { | |
| // Code used to generate a post. | |
| $my_post = array( | |
| 'post_title' => $title_prepend . ' ' . ($i + 1), | |
| 'post_status' => 'publish', | |
| 'post_author' => $author_id, | |
| 'post_type' => 'post', | |
| 'tags_input' => [ 'generated' ], | |
| 'meta_input' => $assoc_args, // Simply passes all key value pairs to posts generated that can be used in testing. | |
| ); | |
| // Insert the post into the database. | |
| wp_insert_post( $my_post ); | |
| $progress->tick(); | |
| } | |
| $progress->finish(); | |
| WP_CLI::success( $desired_posts_to_generate. ' posts generated!' ); // Prepends Success to message | |
| } |
Finale
That’s it. If you want to learn more, see the official WP-CLI documentation. If you want to see all the commands together, see the entire WP-CLI 101 Codefile.

That is great article for me as a beginner level. I think it help me to reach my next level…
Good afternoon Numan, I’m glad I could help.
nice article Scott.
I have website hosted on google cloud & I regularly use WP CLI IMPORT for posts via putty ssh connection.
As we publish 2000-5000 posts daily many times connection interrupts.
So, I’m looking for a way to run wp import trigger whenever we upload xml to a particular folder on server.
Your guidance will help a lot.
As WP CLI acts like any other CLI command one thing you might consider is have a server cronjob run a WP CLI IMPORT command on a timed interval (your trigger). Then use the XML file as a data source for your import command.
Where should you put the class and how should you include it?
Will it get loaded in all of wordpress or just by wp-cli?
Just figuring that out too. Here are the steps to get going:
1) Create a new directory under wp-content/plugins called wps –
[Wordpress root]/wp-content/plugins/wps
2) Place the example file in that directory-
[Wordpress root]/wp-content/plugins/wps/wds-hello-world.php
3) Use the wp CLI to activate the new plugin with this command –
wp plugin activate wds
4) Run the command to test –
% wp wds hello_world
Hello World!
And NOTE – DO NOT DO THIS ON A PRODUCTION WEBSITE! You could open a security hole or crash your whole site. You have been warned!
oh,the most important part is in the last reply to last comment.anyway answered my question