As easy as FOR %%i IN ( %0 ) DO %%~di%%~piperl -x -S %0 %*

Alias on 2008-07-30T11:22:14

Seriously, the things I do for you people.

With the Portable alpha, and feedback starting to roll in, the most obviously blocker for Perl on a Stick is that the batch files produced by pl2bat don't work when Perl isn't in PATH, which for the portable situation is a given.

The trouble with batch programming is that it's such a dead language at this point, it's hard to even find good information about it.

So in Portland, I undertook an expedition to the glorious fantabulous Powell's Books, in the hope that if anyone could find me something, they could.

Alas, the only remain batch file programming book in the United States was stuck in their warehouse in Chicago! :(

Fortunately, Powell's expert Michael Schwern came to my rescue and after some scrounging in the second hand books sections, emerged with just what I needed.

Aside from being extremely old, it also happens to be about the most embarrassing technical book I've ever owned. Pulling it out at the coffee shop in front of an assembled gaggle of Perl/OSCON geeks resulted in a loud chorus of laughter...

http://www.flickr.com/photos/xwrn/2702817504/

And so between this book, some built in Windows help information, and some online tutorials, I've managed to piece together a fix to pl2bat to make the batch script look for and use the perl executable in the same directory as the .bat file, rather than using the PATH version.

The specific line is:

FOR %%i IN ( %0 ) DO %%~di%%~piperl -x -S %0 %*

In a way, I'm quite lucky that it turned out so easy, given how utterly dysfunctional Windows batch is as a "language". By comparison, shell script is elegant and well structured.

The key problem here is that batch doesn't have any string manipulation functions at all, nor does it have much of a file system interface either.

It does (sort of) support variables, which is the "%i" in the above. Except that it's only %i if you use a variable on the command line. Inside a batch script you have to use %%i instead. Don't ask me why, I've never seen any explanation. It just is.

The "perl -x -S" bit is straight forward by comparison...

%0 is the equivalent of $0, and %* is the equivalent of @ARGV.

The tricky bit here is that there's literally no way to turn a string like C:\strawberry\perl\bin\foo.bat into C:\strawberry\perl\bin\perl.exe, so you can call the correct Perl instance.

So instead, we need to badly abuse some evil functionality built into the FOR command/loop.

The FOR command has a bunch of wildly diverse, and utterly insane, ways of working.

But the interesting one is when you use it to iterate over files, like so:

FOR %%variable IN ( file1.txt file2.txt ) DO something

If, and ONLY if, FOR is used to iterate over a list of files, and it knows they are files, then some magic %%variable stuff comes into existance.

These looks like %%~(letter)variable, and let you access different bits or variations of the file name.

The two useful ones in this case is %%~dvariable, which gives us the drive letter for the file in the form "C:", and %%~pvariable which gives the path in the form "\strawberry\perl\bin\".

And thus, by doing a completely useless single value for loop FOR %%i IN ( %0 ) DO we can cause the magic variables we need to come into existance, from which we can just do a simple string concatination (which needs no syntax, because it's just implied) and add perl.exe to the end, which gets us all the way to something that is rediculous, but works.

Of course, this only works post-installation, and wouldn't be usable at all if you called it in the modules blib at build time...

Making THAT works gets even weirder, and will most likely require more thought.

But it does look like I'll have batch stuff working properly by the time YAPC::EU rolls around, making the YAPC::EU "Perl on a Perl on a Stick Stick" much more functional than the American version :)


You overengineered it

snaury on 2008-07-30T15:11:25

Because it's actually as simple as:

"%~dp0perl.exe" -x -S "%~f0" %*

The quotations and "%~f0" are needed in case batch file is in a directory with whitespaces.

I'm afraid it is not quite that simple

jand on 2008-07-31T08:52:55

I'm "stuck" with just a MacBook in Europe right now, so I can't easily test this, but when i was playing around with this last time I noticed that there were several bugs in cmd.exe that prevented the %~ expansion from working correctly. Things like invoking the batch file and explicitly specifying the .bat extension and/or putting quotes around the command name.

You can find my earlier attempt here: http://groups.google.com/group/perl.perl5.porters/msg/b1880b658b5ab73f

It worked for all the test cases I came up with, but is a lot more convoluted than I thought it should.

WSH

jplindstrom on 2008-07-31T14:17:57

Batch certainly sucks, it even has two(!) incomplete sets of escaping rules.

So instead of that complete and utter mess, why not use Windows Scripting Host? That sounds a lot easier.

Re:WSH

snaury on 2008-08-02T17:34:28

A very long ago, when I needed to automate things and didn't know .net/perl/python I was using WSH (via its javascript), and I must say it sucks even more. Especially since windowed/console mode is a global setting, plus it had some funny glitches in child processes execution. So, batch files are much better.