http://www.perlmonks.org?node_id=978445

Ransom has asked for the wisdom of the Perl Monks concerning the following question:

Hey Monks

Got a new stumper. I'm using the perl system command in windows (2003 server), and I'm getting some really strange results. My command boiled down is system("C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli", "-s 143.207.48.60", "-u getinfo", "-p pass");. This same command works great from cmd.exe but not when run from strawberry perl inside the script. I get a connection error which makes me think that my arguments get passed funny.

I've super searched and google-fu'd and realize that this is a rather sticky issue all around. Mostly I'm wondering if there's a way for me to see what command is actually passed and where it's passed to. My specific question is if the argument list format needs to also be slash escaped like the command itself.

I've tried this as a single long string and also removed various arguments to no effect. After reading a bunch of posts I've started to lose hope that this will be resolvable but I figure I'd throw it out there to see if anyone has come across this lately.

Thanks, Ransom

Replies are listed 'Best First'.
Re: Windows System Command
by BrowserUk (Patriarch) on Jun 26, 2012 at 15:24 UTC

    Backslash escaping spaces in command paths won't work. By the time the string is passed to the shell, the backslashes will have disappeared and the command processor will see the first half of the path as the command and the second half as an argurment.

    Try it this way:

    system q[ "C:\\Program Files\\HP Remote System Management\\hprsmcli" - +s 143.207.48.60 -u getinfo -p pass];

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      This is working! I guess I had really hacked up what I was reading around the net. Hindsight says that strings sent to the system don't need spaces slashed, and using q[] is useful for enclosing an entire line in quotes which may contain quotes already. :P Thanks for the quick, accurate response ++!
Re: Windows System Command
by Anonymous Monk on Jun 27, 2012 at 02:31 UTC

    use Win32::ShellQuote qw(:all);

    system quote_system( 'C:\Program\ Files\HP Remote System Management\hprsmcli', qw/ -s 143.207.48.60 -u getinfo -p pass / );

      Seems to be the same solution that BrowserUK offered, just packed in a module. It's a bit overkill for my needs, but thank you.

        Seems to be the same solution that BrowserUK offered, just packed in a module. It's a bit overkill for my needs, but thank you.

        Its not the same, its better. You can use it without ever learning the quoting rules of cmd.exe . You can use it just by learning perl's quoting rules. Its the difference between using a calculator to do multiplication, or pencil and paper :)

        If you're interested see also Behind the GUI lives the Shell and How Command Line Parameters Are Parsed

Re: Windows System Command
by cavac (Parson) on Jun 26, 2012 at 15:42 UTC

    Here's what i usually do in cases like this (except i'm using ActivePerl). I just have a small, "compiled" Perl script that i call instead of the real binary.

    This is from memory, but something like this should do the trick:

    #!/usr/bn/perl use strict; use warnings; open(my $ofh, '>', 'C:/some/path/filename.txt') or die($!); print $ofh join("\n", @ARGV); close $ofh;

    "You have reached the Monastery. All our helpdesk monks are busy at the moment. Please press "1" to instantly donate 10 currency units for a good cause or press "2" to hang up. Or you can dial "12" to get connected directly to second level support."
Re: Windows System Command
by Anonymous Monk on Jun 26, 2012 at 21:14 UTC

    I'm not an expert on cmd.exe, but I suspect your argument list is wrong. It should be:

    system("C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli", + "-s", "143.207.48.60", "-u", "getinfo", "-p", "pass");

    It would be really odd for this tool's command parser to expect "-u getinfo" in argv[2], "-p pass" in argv[3], etc. It is much more standard practice to have the switch (-p) and the value (pass) in different slots in argv.

      All that's required is that -p follows -u which follows -s. I've removed several other optional switches for this example. All these have been tested outside of perl. I find it really odd that arg slots would matter.
        I find it really odd that arg slots would matter.

        The Anonymonk is correct in that traditionally on *nix systems, command lines are broken up on whitespace and each whitepsce delimited token is passed to the process in a separate entry in the char *argv[] array.

        So, what you view as the (single) parameter -p pass, actually gets passed into the process as two separate strings in successive entries in argv[].

        However, all that is by the by on windows systems because Windows passes the entire command line to the process as a single string. Essentially, exactly as the user typed it with some obscure, minor exceptions.

        And traditionally on *nix, when passing parameters to system as a list, each item in the list is placed into a element of argv[] uninspected. Which means that if what you pass is; "-p pass" as a single item in the list, that is how the process will receive it. The trouble is, when the program processes that, it will see the '-p' in (say) argv[5], and then inspect the next item argv[6] looking for "pass" and not find it.

        But when you use the list form of system on Windows, the Perl runtime has to concatenate all the items together into a single string in order to pass it to the program being started. The Perl runtime makes some attempts to do this intelligently, but it often gets it wrong.

        Which is why I advocate not using the list form of system on Windows. It is usually much easier to cut an actually command line -- tested and known to work -- from a console session and paste it into q[].

        Of course, sometimes, some elements of the command line used are not constants and it becomes necessary to interpolate (or concatenate) them with the constant bits to form the command line. In that case, a little more effort is required.

        Using your example, say that user, pass and the IP needed to be derived from variables. I'd do it this way. I'd start by getting the command right on the command line:

        "C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli" -s 143. +207.48.60 -u user -p pass

        Then I paste that into the program and quote the various bits individually and join then with spaces:

        system( join ' ', '"C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli"', '-s', '143.207.48.60', '-u', 'user', '-p', 'pass' );

        Note the use of single quotes, avoiding accidental interpolation and allowing the retention of the required double quotes around the program path without escaping.

        Having checked that it works with the parameters hardcoded, it then becomes a simple step to replace the constants with variables where applicable:

        system( join ' ', '"C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli"', '-s', $ip, '-u', $user, '-p', $pass );

        Now, the only thing left to deal with is parameters that themselves can contain spaces, like filepaths. Example:

        system( join ' ', '"C:\\Program\ Files\\HP\ Remote\ System\ Management\\hprsmcli"', '-s', $ip, '-u', $user, '-p', $pass, '--log', qq["$logfile"] );

        Note: There are various other ways of quoting the path, including using backslashed quotes inside double quotes or using concatenation, but qq[] is what I find the clearest for this.

        For shorter commands, I would use qq[] alone when necessary:

        qq[ "c:\\perl 64\bin\perl.exe" -nle"/this/ and print" "..\some file wi +th spaces.txt" ];
        but that can get unweildy for long commands.

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

        I find it really odd that arg slots would matter.

        I know how to translate that :) "I didn't grok perldoc -f system"

Re: Windows System Command
by BillKSmith (Monsignor) on Jun 27, 2012 at 22:29 UTC

    The book "Mastering Windows XP Home Edition" has a section "Troubleshooting: Command Prompt Errors Involving Special Characters". It suggest several solutions to problems like yours. I prefer to "escape" spaces and other special file name characters with a caret (^). I assume that the same syntax is allowed in newer versions of Windows.

      You cannot (and never could) escape spaces in pathnames with caret (^):

      C:\test>dir \Program^ Files\* The system cannot find the file specified. C:\test>dir "\Program Files\*" Volume in drive C has no label. Volume Serial Number is 8C78-4B42 Directory of C:\Program Files 30/04/2012 19:40 <DIR> . 30/04/2012 19:40 <DIR> .. ...

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      The start of some sanity?