Author image
Developer

How to write a (more useful) Drush command

Welcome to part two of our miniseries on Drush commands! Do check out part one if you haven't already. There you'll see what we've been up to with our pretend module, my_awesome_module.

If you're writing a custom Drush command, it's pretty much guaranteed that you're needing to do something more complicated than write 'Hello World' to your database 5,000 times. Let's take a look at some Drush essentials.

Arguments and Options

You guessed it - just pass in an array. Simply give 'arguments' or 'options', and, for each one, a helpful description.

/**
* Implements hook_drush_command().
*/
function my_awesome_module_drush_command() {
  $items['something-awesome'] = array(
    'description' => 'Does something really useful.',
    'aliases' => array('woah'),
    'arguments' => array(
      'required_arg' => 'Argument description for Drush to show on the help page',
    ),
    'options' => array(
      'useful_option' => 'Option description for Drush to show on the help page',
    ),
  );
  return $items;
}

To take advantage of our new setup, we need some small tweaks to our function. Add in our argument and a line to get our option, and we're good to go:

function drush_something_awesome($required_arg) {
  $my_option   = drush_get_option('useful_option');

Now we can call our Drush command with options and arguments!

drush something-awesome my_arg_value --useful_option=asdf

Validating options

In true Drupal style, there's a hook that allows you to validate the options and arguments input to your command. Simply implement drush_hook_validate() , where hook is the drush function you're validating options for. In there, you can get and check each of your options. Using drush_set_error() you can then output appropriate messages to the console. Try something like this:

/**
* Validation for the something-awesome command.
*/
function drush_something_awesome_validate($required_arg = '') {
  if (empty($required_arg)) {
    return drush_set_error('SOMETHING_AWESOME_REQUIRED_ARG_MISSING', dt('You must specify the required argument.'));
  }

  $useful_option = drush_get_option('useful_option');
  if (empty($useful_option)) {
    return drush_set_error('SOMETHING_AWESOME_NO_OPTION', dt('You must specify a useful option.'));
  }
}

Translated strings

Drush has its own basic version of Drupal's wonderful t() function. It's called dt() and you can use it much like t(), even with replacement arrays. Guide to dt()

Using this, we can redo our options from before as:

'arguments' => array(
      'required_arg' => dt('Argument description for Drush to show on the help page'),
    ),
    'options' => array(
      'useful_option' => dt('Option description for Drush to show on the help page'),
    ),

Using the Batch API

Drupal's Batch API is super powerful and can be called from a variety of scenarios, including Drush commands! For a number of years, you needed a fair bit of additional boilerplate code to help Drush run batches, but in recent versions that's no longer necessary, allowing you to batch just as you would anywhere else. There's just one extra line now:

  drush_backend_batch_process();

This should leave you with something like:

/**
* Drush command to do something awesome.
*/
function drush_something_awesome($required_arg) {
  $my_option = drush_get_option('useful_option');

  // Set ourselves up a nice little batch.
  $batch = array(
    'title' => t('Batching something clever'),
    'operations' => array(
      array('something_awesome_batch', $required_arg, $my_option),
    ),
    'finished' => 'something_awesome_batch_finished',
    'file' => 'where/my/file/is/something_awesome.module',
  );
  batch_set($batch);

  // Process the batch!
  drush_backend_batch_process();
}

You could leave the drush function in your .module file, but it's probably tidier and clearer to pull it out into a separate file, e.g. something_awesome.drush.inc

Previous article in series: