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

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

Two part question:

I'm using IPC::Open3 on Win32 to avoid the shell. Notice how double quotes are handled below. I'm assuming this is a Windows issue and not a Perl issue, correct? Extra points if you can explain the logic of this. ;)

#!/usr/bin/perl -w use strict; use IPC::Open2; my @command = ( 'perl', 'my test.pl', 'first arg1', '"second arg2"', 'word "quoted" and \"backslashed\" word', '"2word "quoted" and \"backslashed\" word"', ); my $pid = IPC::Open2::open2( \*r, \*w, @command ); print "Pid = $pid\n"; print "output:\n"; print while $_ = <r>; print "done\n";

With output on Win32 as:

Pid = 316925 output: 316925 [first arg1] 316925 [second arg2] 316925 [word] 316925 [quoted] 316925 [and] 316925 ["backslashed"] 316925 [word] 316925 [2word quoted and "backslashed" word] 316925 done done

Part two of my IPC::Open3 question:

The docs say to call waitpid($pid,0) to reap the child process -- and indeed the windows process table fills up (64 processes?) if waitpid isn't called.

See, I've got a function that returns a file handle. Under Windows it uses IPC::Open3, otherwise it does a open($fh,'-|') call. So the code that calls this function isn't suppose to know or care about how that file handle is generated.

The question is about when to call waitpid() on Windows. I assume it must be called after calling close() (or after the returned file handle goes out of scope). Is that a correct assumption?

If waitpid() has to be called after the close() that means the function that returns the file handle must store the PID some place. So, seems like I need to tie the handle.

Here's my code. I have a few follow-up questions at the end:

#!/usr/bin/perl -w use strict; my %self; my @command = ( 'perl', 'my test.pl', 'one arg', '"two arg"' ); { my $fh = windows_fork( \%self, @command ); print while <$fh>; } print "out of scope\n"; exit; sub windows_fork { my ( $self, @args ) = @_; require IPC::Open2; my ( $rdrfh, $wtrfh ); # Deal with windows quotes my @command = map { s/"/\\"/g; qq["$_"] } @args; my $pid = IPC::Open2::open2($rdrfh, $wtrfh, @command ); binmode $rdrfh, ':crlf'; tie *handle, 'call_waitpid', $rdrfh, $pid; return \*handle } package call_waitpid; use strict; sub TIEHANDLE { my ( $class, $fh, $pid ) = @_; bless { handle => $fh, pid => $pid, }, $class; } sub READLINE { my ( $self ) = @_; my $fh = $self->{handle}; return <$fh>; } sub DESTROY { my ( $self ) = @_; close $self->{handle}; waitpid $self->{pid}, 0; }

Questions:

  1. Is this how you would deal with this issue. Use a tied handle to track the PID?
  2. In READLINE I cannot do: return <$self->{handle}>; why?

    syntax error at fork.pl line 81, near "<$self->{handle"

  3. In the "windows_fork() sub I'm blessing a typeglob. The problem is it's the same typeglob for every call to windows_fork(). So if windows_fork() is called twice the handles are mixed. I tried using gensym() but couldn't get that to work. I need a way to have more than one file handle active at a time.

my $fh = windows_fork( \%self, 'perl', 'my test.pl', 'first stuff'); my $fg = windows_fork( \%self, 'perl', 'my test.pl', 'other stuff'); print while <$fh>;
Results in "other stuff" from $fh.

Replies are listed 'Best First'.
Re: IPC::Open3 and Win32
by ikegami (Patriarch) on Sep 28, 2004 at 20:21 UTC

    Part 1:

    I'm guessing it's executing join(' ', @command) and the child perl parses it, or the joined string is passed to cmd /c which parses it.

    Part 2:

    1) I like this tying approach. However, I heard tie was really slow compared to a class that do the same thing. You might want to write and use a subclass of IO::Handle instead of tying.

    2) Try not using > inside of <>: <$$self{handle}>.

    3) gensym works great: (tested)

    use IO::Handle; # for gensym my $handle = gensym; tie *$handle, 'call_waitpid', $rdrfh, $pid; return $handle;
Re: IPC::Open3 and Win32
by Velaki (Chaplain) on Sep 28, 2004 at 19:25 UTC

    This is just to comment on the quotes part.

    Windows shell interprets single quotes as string delimiters, but double quotes as literals; so that *nix

    perl -le 'print "Hello, world!" '
    becomes
    perl -le "print \"Hello, world!\" "
    in windows.

    In other words, from the Windows shell, you have to escape double quotes.

    Hope that helped that part,
    -v
    "Perl. There is no substitute."
      > In other words, from the Windows shell, you have to escape > double quotes.

      I believe that IPC::Open3 is suppose to bypass the windows shell -- IIRC it uses system( 1, @_ ), which I'm not clear on how that works on Win32.

      And I understand about the use of quotes on windows shell -- what's weird is the quotes are not needed to protect spaces in parameters (hint that it's avoiding the shell in that case), but if you use double quote within the parameter then the individual words *are* split into multiple parameters -- as if the shell *is* used in that case.

      The issue here is passing user data from one program to another -- and avoiding the risks of the data passing through the shell.