in reply to Construct command portable way

How is that portable? Furthermore, it's buggy and unsafe. Use the multiple argument form of system or open3. Also see IPC::System::Simple.

Replies are listed 'Best First'.
Re^2: Construct command portable way
by Corion (Patriarch) on May 07, 2009 at 06:45 UTC

    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...

      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.

      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).