I was investigating what it would take to make a standard unix script file that looks something like this:
#!drush php-script
<?php
drush_print("this is a simple script");
This works just fine, except for one prerequisite, one bug and one limitation:
Prerequisite: You must put $options['script-path'] = '/'; into your drushrc.php file in order to allow absolute paths to work in php-script.
Bug: When drush runs the line include($script_filename);, php will print the first "shebang" line ("#!drush php-script"). php-cli suppresses this automatically, but "include" does not. Using drush_op('system', 'php drush php-script file') does not work, though, because php will execute in a new environment in which Drupal is not bootstrapped. eval would have to be used after the # line is manually discarded.
Limitation: You must specify the target Drupal site with --root and --uri; it is not possible to use site aliases, because drush expects the site spec to appear before the command name.
I tend to think that writing "#!drush php-script" style php scripts would be useful and Unix-like, and all three of the issues above could be addressed with a bit of code. I'm not sure it's so much better than the existing drush command facility, but it might be a good way for Unix scripters to get a quick start into drush / Drupal php scripting, so maybe I'll pick this up again later.
| Comment | File | Size | Author |
|---|---|---|---|
| #8 | drush-shebang-2.patch | 22.49 KB | greg.1.anderson |
| #5 | drush-shebang.patch | 17.77 KB | greg.1.anderson |
| #4 | shebang-drush.patch | 4.47 KB | greg.1.anderson |
Comments
Comment #1
moshe weitzman commentedNifty. I never knew you could do custom shebang like that.
Comment #2
greg.1.anderson commentedYeah; if you mark the file above executable and run it from bash, then bash will add the filename executed + the command-line args to the shebang line and execute it. So if you run the above as:
./mytest.php --arg=valueThen what runs is
drush php-script ./mytest.php --arg=value. Could be useful to support this in drush someday.Comment #3
greg.1.anderson commentedHm, I just learned something new about shebang myself. It appears that on some (most?) systems, including Linux and Cygwin, parameters are not split. Ergo, the following:
File: testdrush.php
#!drush @site php-scriptwhen executed as
testdrush.php a b c, is equivalent to runningdrush "@site php-script" /path/to/testdrush.php a b c, as everything after the executable ("drush") is interpreted as the first parameter.To further complicate things, the executable is supposed to always be a full path. "#!drush" works on Ubuntu, but it's not guaranteed to work on all platforms. The way to get around this is usually to use /usr/bin/env, like so:
#!/usr/bin/env drush php-scriptHowever, we already know from the above that this will tell env to run "drush php-script", which will fail. :(
There is a solution... coming up.
Comment #4
greg.1.anderson commentedWell, I don't quite have a solution to #3, but here is a work-in progress patch that solves the bug and prerequisite from #0, but ignores the fact that
#!drushis not portable (you can use#!/path/to/drush php-scripton platforms that do not support#!drush, though.n.b. In this patch, the <?php in the script file is optional, but the shebang line must include "drush". I also coded up another very similar version where the <?php was required, but the shebang line did not require "drush". Not sure which is better.
Still thinking about the other stuff.
Comment #5
greg.1.anderson commentedSuccess!
You might think from #3 that information has been lost, and there is
no good way to tell if execve has passed us space-separated arguments
(as opposed to the user quoting the arguement). However, with just
a little bit of introspection, drush can detect that it was launched
from a drush "shebang" script and fix everything up. See the comments
in the patch for more details.
This patch also adds the special '--bootstrap-to-first-arg' option.
This option checks to see if the first user-provided argument is an alias
or site specification; if it is, it will be shifted into the first arguement
position, where it will specify the site to bootstrap. The result of this
is that if your shebang line looks like this:
#!/path/to/drush @shift php-script
Then when you run that script, you can optionally provide an alias such
as @dev as the first argument (e.g. $ ./mydrushscript.php @dev scriptarg1
scriptarg2). Since this is the behavior that one would usually want,
it is default behavior for a canonical script. That is, a script
with a simple shebang line, like so:
#!/path/to/drush
will implicitly have "--bootstrap-to-first-arg" and "php-script" prepended, and will therefore
behave exactly like the first example. To write a script that does not
use --bootstrap-to-first-arg, then the drush command or at least one flag must be explicitly
included, like so:
#!/path/to/drush php-script
The usuall /usr/bin/env trick works for canonical scripts:
#!/usr/bin/env drush
However, it is *not* possible to add additional parameters after "drush"
when using this form. This is due to the limitation of execve, which
will pass drush and everything after it, including the spaces, as a single
argument to env, which in turn attempt to launch "drush php-script" (and fail).
/usr/bin/env is not necessary on some systems; Ubuntu Linux, for example,
allows:
#!drush php-script
This works exactly like the /usr/bin/env variant. Some O.S.s require the
script parameter to be an absolute path, though, so for better flexibility,
you might prefer to standardize on the location where you install drush
and just use the absoulte path to drush in your scripts.
Finally, this patch adds a new function, drush_shift(), which shifts
the next arguement of the arguement list and returns it. drush_get_arguements()
still returns an array that starts with the drush command (php-script)
and ths script being executed, and is followed by all of the user arguments.
drush_shift(), on the other hand, skips these first two arguements and
returns the first user argument the first time it is called.
Some examples:
testdrush.php
$ testdrush.php a b c
$ testdrush.php @gkdev a b c
I expect that this feature will be very popular with people who
are already very familiar with shell scripting, and should be
the perfect "gateway drug" to full drush commands. I also feel
that drush shebang scripts are better than drush commands
for some uses, such as samples in drush_extras that
need to be customized by the user. For example, #681690: Proposal: site-sync and other "site" commands for drush would
fall into this category. Scripts such as these could be placed in
drush_extras/scripts; users can copy these to their own work
folders to customize and use.
Comment #6
moshe weitzman commentedLooks good.
I feel like we should ship with one of these scripts in examples directory. Perhaps another copy of make-me-a-sandwich. I guess we need to warn Windows users that this is not for them (Cygwin understands this?)
Should this be in hook_COMMAND_init() like we did during recent bootstrap revamp?
no 'e' in argument. happens a few times ... typo: 'arument'.
lets copy some docs from this issue to this part of the code. explain why one uses this option.
Comment #7
luchochs commented@greg.perseverancia.anderson:
Bravo!
Comment #8
greg.1.anderson commentedGood call. This updated patch moves the bootstrap_max to the _init() phase. It does the same for other commands (status and core-cli) that also call bootstrap_max().
Fixed typos, enhanced docs, and added examples/helloworld.script. We could perhaps use a couple more examples, but this is a good start.
Comment #9
moshe weitzman commentedNice work. Minor comments below. Feel free to commit afterwards.
I'm wishing that this patch could add more code to script command and less to the includes. It is a bit invasive in that way. Perhaps some of the helper functions can move? If not, thats OK.
typo: latter instead of later
repeated word
Comment #10
greg.1.anderson commentedCommitted. Thanks for the review.
Unfortunately, that code which is in includes must be located there, because it is needed before drush has decided which command will executed. For example, it is possible to make your own custom shebang processor via "#!drush my-php-handler", for example, and process the script file however you wish. I could move the code to includes/shebang.inc, and only include it if needed, but you'd still need a few lines in includes to do that, and there isn't too much code that would go in shebang.inc, so I don't think it's quite worth it.
Comment #11
greg.1.anderson commented