I wrote these 2 functions for migrate module's drush integration. This module needs to spawn a subshell when it looks like php is running out of memory. I imagine that might be a common requirement. The 2 functions re-issue the current command and options in a subshell.

I have not tried this while issueing a remote command.

Do folks think this is useful enough to add to drush itself? I'm inclined to say yes.


/*
 * Spawn a subshell which runs the same command we are currently running.
 */
function drush_migrate_backend_invoke() {
  $args = drush_get_arguments();
  $options = drush_migrate_get_options();
  drush_backend_invoke(implode(' ', $args), $options);
}

/**
 * Get the value of all migrate related options. Used when spawning a subshell.
 *
 * @return
 *   An array of command specific options and their values. 
 */
function drush_migrate_get_options() {
  $options = array();
  $command = drush_parse_command();
  foreach ($command['options'] as $key => $value) {
    // Strip leading --
    $key = ltrim($key, '-');
    $value = drush_get_option($key);
    if (isset($value)) {
      $options[$key] = $value;
    }
  }
  return $options;
}

Comments

greg.1.anderson’s picture

That drush_backend_invoke(implode(' ', $args), $options); doesn't work very well. drush_backend_invoke will explode things apart again, but the splits won't always come at the right place if there are spaces embedded inside of the command. There's another flavor of backend invoke that keeps that args in array form to avoid this problem. When the site alias code uses this function, it assumes that the first arg is the command, which as you mention in #658432: drush_backend_invoke() assumes single word command names is wrong, but works, because backend invoke puts things back together correctly in the end.

greg.1.anderson’s picture

(Above, I meant to say "if there are spaces embedded inside of some of the args", of course.)

In #628996: Concurrently execute a single drush command on multiple sites #6 I posted a new patch that comes closer to unifying the re-dispatch that I'm doing with the version you have above. My code currently reads like this:

function _drush_do_command_redispatch($command, $args, $remote_host = NULL, $remote_user = NULL) {
  $data = _drush_redispatch_get_options();

  // If the path to drush was supplied, then pass it to backend invoke.
  // (Perhaps this should be added to the default handling of backend invoke.)
  $drush_path = drush_get_option('drush-script');
  if (!isset($drush_path)) {
    $drush_folder = drush_get_option('drush');
    if (isset($drush)) {
      $drush_path = $drush_folder . '/drush';
    }
  }
  // Call through to backend invoke.
  drush_log(dt('Begin redispatch via backend invoke'));
  $values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
  drush_log(dt('Backend invoke is complete'));
}

function _drush_redispatch_get_options() {
  // Start off by taking everything from the command line ('options' context)
  $options = drush_get_context('options');
  // If we can parse the current command, then examine all contexts
  // in order for any option that is directly related to the current command
  $command = drush_parse_command();
  if (is_array($command)) {
    foreach ($command['options'] as $key => $value) {
      // Strip leading --
      $key = ltrim($key, '-');
      $value = drush_get_option($key);
      if (isset($value)) {
        $options[$key] = $value;
      }
    }
  }
  return $options;
}

For the get_options routines, there are two differences.

  1. I begin with $options = drush_get_context('options'); instead of an empty array. This insures that all of the options from the command line will be passed along no matter what. This is important because any command-line arg that allowed you to bootstrap and dispatch to the command the first time is going to also be necessary the second time. This is also a fallback position for difference #2, below.
  2. If the command could not be found by drush_parse_command(), then I just skip the code that takes options from any context if they relate to the current command. This, combined with difference #1, allows remote commands to be executed even if the remote command exists only on the remote system, and not on the system it is being called from.

There are also two differences in the re-dispatch code.

  1. I take $command and $args rather than imploding a string and exploding it back to an array again. I'd recommend that you use a wrapper to this wrapper that array-pops $command from drush_get_arguments(); and calls through to my routine.
  2. I look up the path to drush from the command line args and pass it on to backend invoke. This code could be moved into the default handling in backend invoke, if desired.

Other differences I either removed, or migrated elsewhere. The site alias code is now careful to put the 'root' and 'uri' options into the 'options' context instead of the 'alias' context, insuring that these are always picked up by the command re-dispatch function. This is important because backend invoke will pick up the values of root and uri from their bootstrapped values if they are not explicitly set in $data. The site alias code re-dispatches right after the drush bootstrap, so the drupal root and site variables have not been set yet, which would mess up the re-dispatch and cause backend invoke to fail if root and uri were not found inside of $data.

greg.1.anderson’s picture

Assigned: moshe weitzman » greg.1.anderson
Status: Needs review » Fixed

In addition to drush_do_command_redispatch, we also have the handy drush_do_site_command($site_record, $command, $args = array(), $data = array()) for calling a local or remote command given a site record.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.