Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Executing perl program from another perl program and capturing the output

by rgren925 (Beadle)
on Nov 25, 2014 at 02:01 UTC ( #1108296=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks.

I have some code I'm trying to port from *nix to Windows and am stumbling over Windows long names. I've scoured the web and see many references to this, but none have helped.

Here's what I'm trying to do...

my $progPath = cwd(); my $perlPath = $^X; my $args = "-c abc.conf -r"; # then build the command and capture the output... my $output = `$perlPath $progPath/my.pl $args`;
Both the perl and program paths are in "Program Files", so it's tripping over the spaces.

I need to capture the output. I've tried backticks, capture(), qx{}. I've tried many permutations of quotes, escaped quotes, q{}, qq{}, etc. with no luck.

I know I could make it work by converting the paths to Windows 8.3 names.

I'm asking:

1) is it wise to just go ahead and use the 8.3 name and be done with it?

2) is there a way to make this work without doing that and is that the wiser route?

Thanks, Rick

  • Comment on Executing perl program from another perl program and capturing the output
  • Download Code

Replies are listed 'Best First'.
Re: Executing perl program from another perl program and capturing the output
by BrowserUk (Patriarch) on Nov 25, 2014 at 03:35 UTC

    The list form of system won't help you on windows.

    The simple solution is to quote your paths (just as you would at the command line). Ie.:

    my $progPath = cwd(); my $perlPath = $^X; my $args = "-c abc.conf -r"; # then build the command and capture the output... my $output = `\"$perlPath\" \"$progPath/my.pl\" $args`;

    S'not com'cated :)


    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.

      Hi BrowserUk,

      It is my recollection that others have explicitly recommended the list form of system on Windows. Could you explain why it isn't helpful, so i'll not recommend it again in error?

        ould you explain why it isn't helpful,

        Because CreateProcess() is the only way to spawn a new process on windows; and it only accepts a single string containing the runtime arguments.

        If you supply system with the list form of arguments, they just get concatenated together, before being passed as the second argument to CreateProcess().

        There is some attempt to apply intelligence to the concatenation process, but is does not handle paths with spaces (correctly):

        win32_spawnvp(int mode, const char *cmdname, const char *const *argv) { #ifdef USE_RTL_SPAWNVP return spawnvp(mode, cmdname, (char * const *)argv); #else dTHXa(NULL); int ret; void* env; char* dir; child_IO_table tbl; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; DWORD create = 0; char *cmd; char *fullcmd = NULL; char *cname = (char *)cmdname; STRLEN clen = 0; if (cname) { clen = strlen(cname); /* if command name contains dquotes, must remove them */ ...

        About the dumbest thing it could do...


        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 list form of system won't help you on windows.

      If your paths don't include quotes, system can help you on windows, if you use Capture::Tiny

      use Capture::Tiny qw/ capture /; my($stdout, $stderr, $exit) = capture { system { $args[0] } @args; };;

      If you have quotes in your paths then Win32::ShellQuote comes to the rescue

        If ... if ... Capture::Tiny. If ... then Win32::ShellQuote ...

        Just unnecessary! (Ie. A waste of time and effort.)

        If the shell (cmd.exe) requires quotes ...:

        C:\test>"c:\Program Files\Direct Modeling Express 4.0\binx64\7za.exe" +a -y c:\junk.rar "c:\Program Files\Hitman Pro 3.5\*" 7-Zip (A) 4.42 Copyright (c) 1999-2006 Igor Pavlov 2006-05-14 Scanning Creating archive c:\junk.rar Compressing HitmanPro35_x64.exe Everything is Ok

        Then invoking the same command via the shell, via backticks also requires, (and will work with) quotes ... (but use / instead of \ for paths!)

        [0] Perl> $output = `\"c:/Program Files/Direct Modeling Express 4.0/bi +nx64/7za.exe\" a -y c:/junk.rar \"c:/Program Files/Hitman Pro 3.5/*\" +`;; [0] Perl> print $output;; 7-Zip (A) 4.42 Copyright (c) 1999-2006 Igor Pavlov 2006-05-14 Scanning Updating archive c:/junk.rar Compressing HitmanPro35_x64.exe Everything is Ok

        And that's *all* that is required. No ifs; no buts; learn the rules, and it just works.


        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.
      This was exactly what I needed. I had tried just about every combination of quotes and escaped quotes BUT this, naturally. I had tried Win32:GetShortPathName and it worked but, as this is a cross-platform tool, I prefer to avoid platform-specific code paths where possible. Thanks very much!
        I had tried Win32:GetShortPathName and it worked but, as this is a cross-platform tool, I prefer to avoid platform-specific code paths where possible.

        Indeed. I don't have to write cross-platform very often, and I have no problem using well-written modules when required.

        But unlike many others (it seems), my criteria for "when required" extends well beyond 'it exists'. If I don't need it, I don't use it(*).

        I like simple! (Which my wife would probably say, is just as well ... :)

        There are very good reasons for keeping ones dependencies to a minimum.


        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.
Re: Executing perl program from another perl program and capturing the output
by Loops (Curate) on Nov 25, 2014 at 02:58 UTC

    Hi there,

    You don't want to use 8.3 names. You just need to pass each individual parameter to system as a list, instead of together in a string. That way the parameters are already divided up and not further split on space characters. Unfortunately system does not capture its output, but Capture::Tiny is to the rescue in that regard:

    use Capture::Tiny ':all'; my @cmd = ( 'C:\program files\myprog\space invaders.exe', 'parameter 1', 'parameter 2 with spaces' ); my ($stdout, $stderr, $exit) = capture { system @cmd }; print "The return status was $exit\n"; print "The output was $stdout\n";

      You don't want to use 8.3 names ...

      If they have anything resembling unicode you sure do want to use 8.3 if you got them :) otherwise you won't be able to read or execute that file

        Well, I'll have to take your word for that. If true, then Perl does not deserve to be used on Windows. Telling people to use 8.3 filenames at this stage of the game is a sad admission of defeat and a poor implementation.

Re: Executing perl program from another perl program and capturing the output
by BillKSmith (Monsignor) on Nov 25, 2014 at 03:03 UTC
    The 'brute force' solution to problems with long windows names is, as your suggest, to use their short alias. You can learn the short name of a file (or directory) by using the /X option of the dir command. There are other options, but I usually prefer this.
    Bill

      by using the /X option of the dir command.

      :) Seeing how the OPs issue is not howing how to call the shell and stuff, using the shell to discover shortname is going to be hard :) Win32::GetShortPathName otoh is much easier

Re: Executing perl program from another perl program and capturing the output
by Anonymous Monk on Nov 25, 2014 at 02:57 UTC

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (1)
As of 2023-09-23 17:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?