in reply to Execute command with spaces in perl

I would strongly recommend you use a module to do this for you, instead of trying to do it yourself. For example, there's capturex from IPC::System::Simple, or IPC::Run3, which uses Win32::ShellQuote under the hood on Windows:

use warnings; use strict; use IPC::Run3 'run3'; my $cmd = 'C:\Program Files\Mozilla Firefox\firefox.exe'; run3 [$cmd,'--version'], undef, \my $out or die "run3: $!"; die "\$?=$?" if $?; chomp $out;

If you want to stick with piped open, you could also use Win32::ShellQuote directly to do the quoting for you. I wrote about the above options, as well as the pitfalls with running external commands, at length here, with example code. In this case, since you're running a fixed external command with one or more arguments, you can also use the LIST form of open:

use warnings; use strict; my $cmd = 'C:\Program Files\Mozilla Firefox\firefox.exe'; open my $fh, '-|', $cmd, '--version' or die $!; my $out = do { local $/; <$fh> }; # slurp close $fh or die $! ? $! : "\$?=$?"; chomp $out;

Note how I am also checking close for errors, this is necessary for a piped open. I'm also slupring the entire output of the command into my variable $out; in your code you're only fetching the first line of output, despite that you've named your variable $aarray.

Replies are listed 'Best First'.
Re^2: Execute command with spaces in perl
by Lotus1 (Vicar) on Aug 27, 2018 at 13:33 UTC

    I got your example to work using 7-zip as shown below. When Windows sees a string with spaces it assumes you are giving it a list of a program and options so it tries to execute the first item in the list. That first item is 'C:\Program' unless you give it double quotes to show the full path to the executable. (Edit: In Corion's answer to the OP quotes are done correctly and fix this issue.)

    use warnings; use strict; ## works: no command line options my $cmd1 = 'C:\Program Files\7-Zip\7z.exe'; ## works: Double quotes tells Windows where the first argument, the co +mmand, ends. my $cmd2 = '"C:\Program Files\7-Zip\7z.exe" l Win32-Console-0.10.tar.g +z'; ## doesn't work: gives message about 'C:\Program' is not recognized... my $cmd3 = 'C:\Program Files\7-Zip\7z.exe l Win32-Console-0.10.tar.gz' +; foreach my $cmd ( $cmd1, $cmd2, $cmd3 ) { open my $fh, "$cmd|" or die $!; my $out = do { local $/; <$fh> }; # slurp close $fh or die $! ? $! : "\$?=$?"; print "<<<$out>>>\n"; } __DATA__ <<< 7-Zip 18.05 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2018-04-30 Usage: 7z <command> [<switches>...] <archive_name> [<file_names>...] <Commands> a : Add files to archive b : Benchmark d : Delete files from archive e : Extract files from archive (without using directory names) h : Calculate hash values for files i : Show information about supported formats l : List contents of archive rn : Rename files in archive t : Test integrity of archive u : Update files to archive x : eXtract files with full paths <Switches> -- : Stop switches parsing @listfile : set path to listfile that contains file names -ai[r[-|0]]{@listfile|!wildcard} : Include archives -ax[r[-|0]]{@listfile|!wildcard} : eXclude archives -ao{a|s|t|u} : set Overwrite mode -an : disable archive_name field -bb[0-3] : set output log level -bd : disable progress indicator -bs{o|e|p}{0|1|2} : set output stream for output/error/progress line -bt : show execution time statistics -i[r[-|0]]{@listfile|!wildcard} : Include filenames -m{Parameters} : set compression Method -mmt[N] : set number of CPU threads -mx[N] : set compression level: -mx1 (fastest) ... -mx9 (ultra) -o{Directory} : set Output directory -p{Password} : set Password -r[-|0] : Recurse subdirectories -sa{a|e|s} : set Archive name mode -scc{UTF-8|WIN|DOS} : set charset for for console input/output -scs{UTF-8|UTF-16LE|UTF-16BE|WIN|DOS|{id}} : set charset for list fi +les -scrc[CRC32|CRC64|SHA1|SHA256|*] : set hash function for x, e, h com +mands -sdel : delete files after compression -seml[.] : send archive by email -sfx[{name}] : Create SFX archive -si[{name}] : read data from stdin -slp : set Large Pages mode -slt : show technical information for l (List) command -snh : store hard links as links -snl : store symbolic links as links -sni : store NT security information -sns[-] : store NTFS alternate streams -so : write data to stdout -spd : disable wildcard matching for file names -spe : eliminate duplication of root folder for extract command -spf : use fully qualified file paths -ssc[-] : set sensitive case mode -sse : stop archive creating, if it can't open some input file -ssw : compress shared files -stl : set archive timestamp from the most recently modified file -stm{HexMask} : set CPU thread affinity mask (hexadecimal number) -stx{Type} : exclude archive type -t{Type} : Set type of archive -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName] : Update options -v{Size}[b|k|m|g] : Create volumes -w[{path}] : assign Work directory. Empty path means a temporary dir +ectory -x[r[-|0]]{@listfile|!wildcard} : eXclude filenames -y : assume Yes on all queries >>> <<< 7-Zip 18.05 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2018-04-30 Scanning the drive for archives: 1 file, 29375 bytes (29 KiB) Listing archive: Win32-Console-0.10.tar.gz -- Path = Win32-Console-0.10.tar.gz Type = gzip Headers Size = 33 Date Time Attr Size Compressed Name ------------------- ----- ------------ ------------ ----------------- +------- 2013-11-28 16:16:37 ..... 153600 29375 Win32-Console-0.1 +0.tar ------------------- ----- ------------ ------------ ----------------- +------- 2013-11-28 16:16:37 153600 29375 1 files >>> With $cmd3 the following goes to STDERR:' 'C:\Program' is not recognized as an internal or external command, operable program or batch file. $?=256 at C:\usr\pm\pipe.pl line 11.

      See Re^2: Having to manually escape quote character in args to "system"? for an interesting discussion on the issue of command-line arguments on Windows. Basically, command line arguments are not passed as a list, but a single big string, and it's up to the called command, not the shell, to interpret that string, and different commands may do that differently.

Re^2: Execute command with spaces in perl
by naveenp5 (Initiate) on Aug 27, 2018 at 12:06 UTC

    Thanks for your suggestion. The '-|", LIST pipe seems not working on Windows. My script should be able to execute seamlessly between Linux and Windows. Instead when I use the STRING pipe, as mentioned here: https://www.perlmonks.org/bare/?node_id=902198, it again breaks splitting on the space: 'C:\Program' is not recognized as an internal or external command, operable program or batch file.

    my $cmd = 'C:\Program Files\Mozilla Firefox\firefox.exe --version'; chomp($cmd); open my $fh2, "$cmd|" or die $!; my $out = do { local $/; <$fh2> }; # slurp close $fh2 or die $! ? $! : "\$?=$?"; chomp $out;
      The '-|", LIST pipe seems not working on Windows.
      open my $fh2, "$cmd|" or die $!;
      it again breaks splitting on the space

      I tested my code before posting, and the '-|', LIST form should work on Windows (Update: at least on Perl 5.22 and up). If it's not working for you, please show the code you're trying and the exact error messages you're getting (How do I post a question effectively? and Short, Self-Contained, Correct Example). The code you posted here is not the '-|', LIST form, which it why it isn't working - why aren't you using the code I showed? With a single string argument, you need to do the quoting correctly; I explained the difference between the two in the node I linked to earlier.

      If you need --version to be variable as well, you could do this:

      use warnings; use strict; my @cmd = ('C:\Program Files\Mozilla Firefox\firefox.exe', '--version'); die "\@cmd must have more than one element" unless @cmd>1; open my $fh, '-|', @cmd or die $!; my $out = do { local $/; <$fh> }; # slurp close $fh or die $! ? $! : "\$?=$?"; chomp $out;

      Note I've added the check for @cmd having more than one element to make sure the LIST form is being used.

      If for whatever reason you really need the string form, then you'll have to use better quoting, like what is provided by Win32::ShellQuote, or the other modules I named.

      My script should be able to execute seamlessly between Linux and Windows.

      The code I showed should work fine on both OSes, except of course for the filename of the Firefox executable; if you've having trouble on Linux, again, please show exactly what that trouble is. For platform-independent filename handling, I recommend the core module File::Spec, or with a nicer interface Path::Class.

        For your second example the output on Windows is:

        List form of pipe open not implemented at C:\usr\pm\pipe2.pl line 5.

        This is discussed at this node as mentioned by naveenp5.