Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^2: Construct command portable way

by Corion (Patriarch)
on May 07, 2009 at 06:45 UTC ( [id://762498]=note: print w/replies, xml ) Need Help??


in reply to Re: Construct command portable way
in thread Construct command portable way

Of course, the multiple argument form of system isn't all that portable, either. On Windows, system(@ARGS) is system("@ARGS"), so you need to take proper care of quoting. On more unixish operating systems, you cannot quote the commands if you're going to use system(LIST), so some more care than in the OP is needed.

Of course it would be nice if Win32 system() knew when to add double quotes around strings...

Replies are listed 'Best First'.
Re^3: Construct command portable way
by ikegami (Patriarch) on May 07, 2009 at 10:26 UTC

    On Windows, system(@ARGS) is system("@ARGS"), so you need to take proper care of quoting.

    No it's not, so no you don't.

    >perl -e"@cmd = ('perl', '-le print for @ARGV', 'foo', 'bar'); system +@cmd;" foo bar >perl -e"@cmd = ('perl', '-le print for @ARGV', 'foo bar'); system @cm +d;" foo bar

    The Windows command line cannot handle some characters. You need to verify for their presence, but you won't be able to quote them.

    Of course it would be nice if Win32 system() knew when to add double quotes around strings...

    ActivePerl does since 5.6.1. Can't verify Perl in general.

      This is most weird. In your case, with the hard-coded perl, it works as expected (5.8.3 here). And the following fails, as supported by your assertion:

      > perl -e"@cmd = ('perl ', '-le print for @ARGV', 'foo bar'); system @ +cmd" Der Befehl ""perl "" ist entweder falsch geschrieben oder konnte nicht gefunden werden.

      But the following does not fail:

      >perl -le "print $^X;@cmd = ($^X.' ', '-le print for @ARGV', 'foo bar' +); system @cmd" C:\Programme\Perl\bin\perl.exe foo bar

      So my guess is that $cmd[0] is inspected for whitespace or quotes, and if it contains either, an intermediate shell is invoked. Which is just a horrible kludge, especially when most programs live (under some languages) under C:\Program Files\, which already contains whitespace. And there, quoting becomes necessary:

      >perl -le "@cmd = (chr(34).$^X.' '.chr(34).' -le print+1 for @ARGV', ' +foo bar'); system @cmd" 1

      It fails (properly) when the first parameter does not start with quotes, again:

      >perl -le "@cmd = ($^X.' -le print+1 for @ARGV', 'foo bar'); system @c +md" Der Befehl ""C:\Programme\Perl\bin\perl.exe -le print+1 for @ARGV"" is +t entweder falsch geschrieben oder konnte nicht gefunden werden.

        So my guess is that $cmd[0] is inspected for whitespace or quotes, and if it contains either, an intermediate shell is invoked.

        That doesn't explain the difference at all. Both 'perl ' and $^X.' ' contain a space.

        And there, quoting becomes necessary:

        Not at all. You're mixing the single and multi-arg form for system. Quoting is not the bug. (It shouldn't work with quoting either.)

        In IPC::Exe, I do the following:
        # non-Unix: escape command so that it feels Unix-like my @cmd = $non_unix ? map { (my $x = $_) =~ s/(\\"|")/$1 eq '"' ? '\\"' : '\\\\\\"'/ge; qq("$x"); } @_ : @_;

        before exec { $cmd[0] } @cmd; (See the quoting: test in t/02_IPC-Exe.t)

        This helps to get around most of the quoting issues under Win32.
Re^3: Construct command portable way
by afoken (Chancellor) on May 07, 2009 at 07:17 UTC

    Just adding double quotes is not enough, not even on Windows. How to you pass a double quote as argument to the invoked program (system('/usr/local/bin/foo','-bar','"','-baz','""'))? You need some kind of escaping, and that becomes more and more ugly while the distance to a classic unix /bin/sh grows. I've heard that cmd.exe uses the ^ for some escaping, while command.com just does not have any escaping at all.

    Adding double quotes also is not safe on Unix-like systems, as it does not disable shell interpolation, so you probably want to use single quotes there, or better: Use system(@ARGS).

    I agree that system(@ARGS) on Windows should emulate system(@ARGS) on Unix-like systems, preferably by not messing with cmd.exe/command.com at all. Unfortunately, the Windows API does not offer a way to pass more than a single string to an invoked program, and splitting that string into C's argv is left to the application (or its runtime library). Avoiding command.com/cmd.exe is only the first step.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      system(LIST) avoids cmd.exe on Win32 already, if possible, just like it avoids the shell under unixish systems. Unfortunately, a common mechanism for parsing the command line has found its way into Windows only as CommandLineToArgvW, but as I see now, it shouldn't be hard to use this "standard" way of parsing the command line everywhere except Win 9x (which I still consider a loss for Perl).

        it shouldn't be hard to use this "standard" way of parsing the command line everywhere except Win 9x

        It would "just" require all existing programs to drop whatever command line parsing they use and use CommandLineToArgvW instead, converting the command line to Unicode and the returned elements back from Unicode in non-Unicode applications.

        Windows argument passing simply sucks. Each program can and has to implement its own parsing routine. Most programs rely on the runtime library to handle the parsing. For them, we "only" have to handle the different parsers of the various runtime libraries (all Borland DOS and Windows compilers/RTLs ever released, all Microsoft DOS and Windows compilers/RTLs ever released, and all those special compilers/RTLs like DJGPP, Phar Laps, 4GW, MinGW, ...). Other programs may use Windows API functions like CommandLineToArgvW. For the remaining programs that implement their own parsing routine, we are simply lost. There is no standard for quoting or parsing.

        A rather drastical approach would be to limit system(LIST) to a list of exactly one or two arguments, where the second argument would be the entire command line passed to the program via CreateProcess() or similar functions. This represents the underlying API quite well, but it would break nearly all perl code that uses system(LIST).

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://762498]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (5)
As of 2024-03-29 12:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found