The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Win32::CommandLine - Retrieve and reparse the Win32 command line

VERSION

This document describes Win32::CommandLine ($Version: 0.3.13.9977 $).

SYNOPSIS

        @ARGV = Win32::CommandLine::argv() if eval { require Win32::CommandLine; };

        _or_

        use Win32::CommandLine qw( command_line );
        my $commandline = command_line();
        ...

DESCRIPTION

This module is used to reparse the Win32 command line, automating better quoting and globbing of the command line. Globbing is full bash POSIX compatible globbing, including subshell expansions. With the use of the companion script (xx.bat) and doskey for macro aliasing, you can add full-fledged bash compatible string quoting/expansion and file globbing to any Win32 command.

This module is compatible with both cmd.exe and 4nt/tcc shells.

        [cmd.exe]
        doskey type=call xx type $*
        type [a-c]*.pl
        doskey perl=call xx perl $*
        perl -e 'print "test"'          [o/w FAILS without commandline reinterpretation]

        [TCC/TCMD/4NT]
        alias type=call xx type
        type [a-c]*.pl
        alias perl=call xx perl
        perl -e 'print "test"'          [o/w FAILS without commandline reinterpretation]

Note the bash compatible character expansion and globbing available, including meta-notations a{b,c}*.

String/character expansion:

    '...' => literal (no escapes and no globbing within quotes)

    $'...' => ANSI C string escapes (\a, \b, \e, \f, \n, \r, \t, \v, \\, \', \n{1,3}, \xh{1,2}, \cx; all other \<x> =>\<x>), no globbing within quotes

    "..." => literal (no escapes and no globbing within quotes)

    $"..." => same as "..."

    $( ... ) => subshell expansion

    $("...") => subshell expansion

Globbing:

    \ Quote the next metacharacter

    [] Character class

    {} Multiple pattern

    * Match any string of characters

    ? Match any single character

    ~ Expand to current user home directory

    ~<name> Expands to user <name> home directory for any defined user [ ONLY if {Win32, Win32::Security::SID, Win32::TieRegistry} are installed; o/w no expansion => pull off any leading non-quoted ~[name] (~ followed by word characters) => replace with home dir of [name] if exists, otherwise replace the characters)

    ~<text> Expands to value of environment variable "~<text>" (if defined) [OVERRIDES ~<name> expansion] #?? verify and document which has priority

INSTALLATION

To install this module, run the following commands:

        perl Build.PL
        ./Build
        ./Build test
        ./Build install

Or, if you're on a platform (like DOS or Windows) that doesn't require the "./" notation, you can do this:

        perl Build.PL
        Build
        Build test
        Build install

The standard make idiom ( "perl Makefile.PL" -> "make" -> "make test" -> "make install") is also available (though deprecated) via a Makefile.PL passthrough script. Module::Build is still ultimately required for installation, and Makefile.PL will offer to download and install Module::Build if it is missing from your current installation.

Alternatively, using the standard make idiom (if you do not have Module::Build installed):

        perl Makefile.PL
        make
        make test
        make install

(On Windows platforms you should use nmake instead.)

PPM installation bundles should also be available in the standard PPM repositories (i.e. ActiveState, trouchelle.com [http://trouchelle.com/ppm/package.xml]).

Note: On ActivePerl installations, './Build install' will do a full installation using ppm (see ppm).

INTERFACE

SUBROUTINES/METHODS

command_line( )

command_line() returns the full Win32 command line as a string.

argv( )

argv() returns the reparsed command line as an array.

parse( $ )

parse() returns the parsed argument string as an array.

RATIONALE

Attempts were made using Win32::API and Inline::C.

Win32::API attempt (causes GPFs):

    @rem = '--*-Perl-*--
    @echo off
    if "%OS%" == "Windows_NT" goto WinNT
    perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
    goto endofperl
    :WinNT
    perl -x -S %0 %*
    if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
    if %errorlevel% == 9009 echo You do not have Perl in your PATH.
    if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
    goto endofperl
    @rem ';
    #!/usr/bin/perl -w
    #line 15

    use Win32::API;

    Win32::API->Import("kernel32", "LPTSTR GetCommandLine()");

    my $string = pack("Z*", GetCommandLine());

    print "string[".length($string)."] = '$string'\n";

    # ------ padding --------------------------------------------------------------------------------------

    __END__
    :endofperl

Unfortunately, Win32::API causes a GPF and Inline::C is very brittle on Win32 systems (not compensating for paths with embedded strings).

See URLref: http://www.perlmonks.org/?node_id=625182 for a more full explanation of the problem and initial attempts at a solution.

IMPLEMENTATION and INTERNALS

#=h#ead2 wrap_GetCommandLine() # #[XS] Use C and Win32 API to get the command line.

DIAGNOSTICS

Error message here, perhaps with %s placeholders

[Description of error here]

Another error message here

[Description of error here]

[Et cetera, et cetera]

CONFIGURATION AND ENVIRONMENT

Win32::CommandLine requires no configuration files or environment variables.

Optional Environment Variables

[???] $ENV{NULLGLOB} = 0/1 => override default 'nullglob' setting

[???] $ENV{WIN32_COMMANDLINE_RULE} = "sh" | "bash" (case doesn't matter) => argv will parse in "sh/bash" manner if set to "default"|"undef" - will warn (not carp) if value unrecognized

DEPENDENCIES

None.

INCOMPATIBILITIES

None reported.

% issues => x %%%% => %

BUGS AND LIMITATIONS

Please report any bugs or feature requests to bug-test-command at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Win32-CommandLine. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

Brackets ('{' and '}') and braces ('[' and ']') must be quoted to be matched literally. This may be a gotcha for some users, although if the filename has internal spaces, the standard Win32 shell (cmd.exe) will automatically surround the entire path with spaces (which corrects the issue).

GOTCHA: ** Special shell characters (shell redirection [ '<', '>' ] and continuation '&') characters must still be **double-quoted**. The CMD shell does initial parsing and redirection/continuation (stripping away everything after I/O redirection and continuation characters) before any process can get a look at the command line.

GOTCHA: %<x> is still replaced by ENV vars and %% must be used to place single %'s in the command line, eg: >perl -e "use Win32::CommandLine; %%x = Win32::CommandLine::_home_paths(); for (sort keys %%x) { print qq{$_ => $x{$_}\n}; }"

GOTCHA: Some programs expect their arguments to maintain their surrounding quotes (eg, <perl -e 'print "x";'> doesn't work as expected).

GOTCHA: {4NT/TCC/TCMD} The shell interprets and _removes_ backquote characters before executing the command. You must quote backquote characters with _double-quotes_ to pass them into the command line (eg, {perl -e "print `dir`"} NOT {perl -e 'print `dir`'} ... the single quotes do not protect the backquotes which are removed leaving just {dir}). ??? fix this by using $ENV{CMDLINE} which is set by TCC? => attempts to workaround this using $ENV{CMDLINE} fail because TCC doesn't have control between processes and can't set the new CMDLINE value if one process directly creates another (and I'm not sure how to detect that TCC started the process) -- can try PPIDs if Win32::API is present... => DONE [2009-02-18] [seems to be working now... if Win32::API is available, parentEXE is checked and $ENV{CMDLINE} is used if the parent process matches 4nt/tcc/tcmd]

GOTCHA: Note this behavior (ending \" => ", which is probably not what is desired or expected in this case (? what about other cases, should this be "fixed" or would it break something else?) C:\...\perl\Win32-CommandLine >t\prelim\echo.exe "\\sethra\C$\"win* [0]\\sethra\C$"win*

Please report any bugs or feature requests to bug-Win32-CommandLine@rt.cpan.org, or through the web interface at http://rt.cpan.org.

No bugs have been reported.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Win32::CommandLine

You can also look for information at:

    * AnnoCPAN: Annotated CPAN documentation

      http://annocpan.org/dist/Win32-CommandLine

    * CPAN Ratings

      http://cpanratings.perl.org/d/Test-Command

    * RT: CPAN's request tracker

      http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-Command

    * Search CPAN

      http://search.cpan.org/dist/Test-Command

#EX from 'Touch' #You can find documentation for this module with the perldoc command. # # perldoc Touch # # #You can also look for information at: # #* AnnoCPAN: Annotated CPAN documentation # # http://annocpan.org/dist/Touch #* CPAN Ratings # # http://cpanratings.perl.org/d/Touch #* RT: CPAN's request tracker # # http://rt.cpan.org/NoAuth/Bugs.html?Dist=Touch #* Search CPAN # # http://search.cpan.org/dist/Touch

ACKNOWLEDGEMENTS

#EX #Test::Builder by Michael Schwern allowed me to focus on the specifics related to testing system commands by making it easy to produce proper test output.

AUTHOR

Roy Ivy III <rivy[at]cpan[dot]org>

LICENSE AND COPYRIGHT

Copyright (c) 2007-2009, Roy Ivy III <rivy[at]cpan[dot]org>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.

DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE ''AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

This began as a simple need to reparse the commandline and grew into an odyssey to lend some bash shell magic to the CMD shell (and, additionally, then make it compatible with the excellent (and free) TCC-LE shell from JPSoft).

TODO

Check VCC compilation. Currently, after vcvars.bat setup: 1) compilation proceeds to completion without error, 2) loading the .dll causes a GPF[ perl.exe - Unable to Locate Component == This application has failed to start because MSVCR90.dll was not found. Re-installing the application may fix this problem. ]

Note that a similar GPF occurs for 'test.exe' when test.exe.manifest is removed => [ perl.exe - Unable to Locate Component == This application has failed to start because MSVCR90.dll was not found. Re-installing the application may fix this problem. ]

[test.c] int main(int argc, char **argv, char **env) { } [test.exe.manifest] <?xml version='1.0' encoding='UTF-8' standalone='yes'?> <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level='asInvoker' uiAccess='false' /> </requestedPrivileges> </security> </trustInfo> <dependency> <dependentAssembly> <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> </dependentAssembly> </dependency> </assembly>

SOLUTION (why needed? and is it fixed with later v of ActivePerl?)

** create perl.exe.manifest in same directory as perl.exe executable [c:\perl\bin]

[perl.exe.manifest] <?xml version='1.0' encoding='UTF-8' standalone='yes'?> <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level='asInvoker' uiAccess='false' /> </requestedPrivileges> </security> </trustInfo> <dependency> <dependentAssembly> <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> </dependentAssembly> </dependency> </assembly>