EDIT: The below applies to Drush version 2. Drush 3's site aliasing makes life a lot easier, as there's a built-in alias which means "all sites." So running a command across all sites in a multisite is as easy as:
drush @sites <Drush command>
You'll be interactively prompted on whether you really want to run the command on all your sites; to automatically skip this (for non-interactive cron run scripts, for example), add a `-y` flag to the command. For example, you can create a root crontab entry which looks like:
5 5,11,15,21 * * * </path/to/php> -f </path/to/drush.php> -- -r </path/to/Drupal/root> @sites core-cron -y
…to run cron four times a day. Much easier! (But note this bug.)
Now back to the original article.
I recently experimented with the Aegir project. It didn't seem to work very well for us, though to be fair it may be due to our nonstandard server setup (FreeBSD instead of Linux; Lighttpd instead of Apache) than any fault with Aegir.
Anyway, installing Aegir meant I had to install Drush. I've been meaning to try Drush one of these days, but it never came about. Of course, now that I have, I'm wishing I would have tried it a long time ago!
One of the most interesting features of Aegir to me was its ability to run cron and database updates across multiple sites, and even keep modules updated. What it's really doing, though, is acting as a wrapper to Drush, which itself is capable of doing this, but to only one site at a time. But it should be possible to do that with the magic of the Unix command line, right? After a bit of experimentation, I found the magic formula…
find <path/to/sites/dir> -depth 1 -type d -not -name all | sed -e 's/.*\///g' | xargs -I foo -t <path/to/drush.php> <Drush command> -r <path/to/root/drupal/dir> -l foo
EDIT: The above ought to work on BSD, but if you're using Linux, you'll probably have to replace
-depth 1 with
-mindepth 1 -maxdepth 1 as per naught101's comment below.
You should be able to run this without installing any special packages or anything on your system; if it can run Drupal, it can run this. But for God's sake, you're going to back up your site's code and databases before running this, right? Just because it worked for me doesn't mean it's not going to cause three million dollars' worth of fire damage at your datacenter when you run it.
Let's break it down so that you understand what it does. (You're smart and don't just copy-and-paste stuff from web sites into your terminal without understanding what it does… right? Right?) The
find command finds files meeting certain criteria; in this case, we're finding the paths of each individual site directory. Its parameters/flags are odd because the file path comes before the flags, and the flags use
-flag param format instead of
-f param format or
--flag="param" format (are there technical terms for those formats?)… Anyway, we want it to find things in our Drupal installation's sites directory, so we pass that as the first parameter. The
-depth 1 part tells it not to search any deeper than the sites directory. The
-type d part tells it to find directories; other sorts of files (including symlinks) will be excluded. The
-not -name all part tells it to exclude the "all" directory, since technically there is no "all" site. (The "default" directory will be included, but that's a good thing, since there is a default site.) If you run just this command by itself, the output will look something like
That's all well and good, but we only want to pass the last part of each line ("default", "example.com", "example.org") to Drush. So the next part uses the
sed program with a regular expression to strip out any slashes, and any characters which are before a slash.
sed is a bit of an odd program; it's like a non-interactive text editor which only works by taking text form standard input, running commands on it, and spitting it out to standard out. It's pretty complicated and can do some pretty arcane stuff if you can master it… but there's no shame in just wussing out like me and just using it to run a standard regular expression. Anyway, we now have
xargs is another odd command which lets us use stuff from standard in as arguments for other commands. In this case, with the
-I foo part, we're telling it to take one line from standard input and plug it in for "foo" where it appears in the command to come. The
-t simply tells
xargs to print the full command it's creating right before it's running, which aids in debugging and such - you may omit it if you wish. Finally, we specify the path to drush.php and give it a command. (When testing, I recommend using a relatively innocuous command like "status" - this won't change your site's database or files any when run. If you're new to Drush, run the drush.php script without any commands or flags and it'll print out a list of commands and some brief descriptions of what they do.) The
-r <path/to/root/drupal/dir> tells Drush where your Drupal directory is, and the
-l foo part tells Drush which site to run the command on - remember, "foo" will be replaced with the site. Sticking with our previous examples, Drush will be run three times - once with
-l default, once with
-l example.com, and finally once with
I've successfully used this to run cron tasks across all of our sites on our new server at once. I haven't tried it with module or core updates yet - mostly because none have appeared for our sites since I figured this out - but I imagine it'll work so long as we switch to root first or sneak in
sudo somewhere in there.