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

I'm writing scripts which need to execute some other programs, and look at the stdout and stderr from said calls. My scripts, in turn, are to be called by a 3rd party, closed source program.

I've been using IPC::Open3 for this, and things work great when run from the commandline. But when invoked by the 3rd party program, the call to open3 fails with the following messsage: open3: Can't call method "close" on an undefined value at c:/apps/perl/lib/IPC/Open3.pm line 368.

I whipped up a small test script to illustrate the problem. The 3rd party program doesn't display stdout or stderr, hence why I'm writing everything to files. Also, the "#------#" lines are just to show where the script starts and ends in this post, and are not present in the script

#------# Begin code for test_open3.pl #!c:/apps/perl/bin/perl use IPC::Open3 qw( open3 ); use strict; use warnings; $| = 1; open DBG, ">debug2.log" or die $!; eval { my @pids; my @cmd1 = ( 'c:/Program Files/TortoiseSVN/bin/svn.exe', '--ve +rsion', '-- quiet'); my $nul = $^O eq 'MSWin32' ? 'nul' : '/dev/null'; open(CHILD_STDERR, '>', 'error.txt') or die "Could not open CH +ILD_STDERR for writing ($!)"; open(CHILD_STDOUT, '>', 'out.txt') or die "Could not open CHIL +D_STDOUT for writing ($!)"; push @pids, open3(undef, '>&CHILD_STDOUT', '>&CHILD_STDERR', @ +cmd1); waitpid($_, 0) for @pids; print "Child exited with value ", $? >> 8, "\n"; #close(CHILD_STDIN) or die "Could not clost STDIN ($!)"; close CHILD_STDOUT or die "Could not close STDOUT"; open(CHILD_STDOUT, '<', 'out.txt') or die "Could not open CHILD_ST +DOUT for reading ($!)"; my @out = <CHILD_STDOUT>; close CHILD_STDOUT or die "Could not close STDOUT ($!)"; close CHILD_STDERR or die "Could not close STDERR"; open(CHILD_STDERR, '<', 'err.txt') or die "Could not open CHILD_ST +DERR for reading ($!)"; my @err = <CHILD_STDERR>; close CHILD_STDERR or die "Could not close STDERR ($!)"; print "----OUT----\n", join("\n", @out), "----OUT----\n"; print "----ERR----\n", join("\n", @err), "----ERR----\n"; print DBG "Done!\n"; }; if ($@) { print DBG $@; close DBG; exit 1; } close DBG; #------#

The above code yields open3: Can't call method "close" on an undefined value at c:/apps/perl/lib/IPC/Open3.pm line 368. inside the debug2.log file.

I tried a slightly different approach with this test script:

#------# begin code for test9.pl #!c:/apps/perl/bin/perl use IPC::Open3 qw( open3 ); use strict; use warnings; open DBG, ">debug2.log" or die $!; eval { my @cmd1 = ( 'c:/Program Files/TortoiseSVN/bin/svn.exe', '--versio +n', '--quiet'); print "Running ", join(" ", map { "'$_'" } @cmd1), "\n"; open my $old_stdout, '>&', STDOUT or die "Couldn't dup stdout! ($! +)"; open my $old_stderr, '>&', STDERR or die "Couldn't dup stderr! ($! +)"; open STDOUT, '>', 'out.txt' or die "Couldn't redirect stdout to fi +le! ($!)"; open STDERR, '>', 'err.txt' or die "Couldn't redirect stderr to fi +le! ($!)"; system(@cmd1); close STDOUT; close STDERR; open STDOUT, '>&', $old_stdout or die "Could not re-connect to std +out! ($!)"; open STDERR, '>&', $old_stderr or die "Could not re-connect to std +err! ($!)"; open FH, '<', 'out.txt' or die "Could not open out.txt! ($!)"; open FH2, '<', 'err.txt' or die "Could not open err.txt! ($!)"; my ($out, $err); { local $/; $out = <FH>; $err = <FH2>; } close FH; close FH2; print "----OUT----\n", $out, "\n----OUT----\n"; print "----ERR----\n", $err, "\n----ERR----\n"; #print join("\n", @err), "\n"; }; if ($@) { print DBG $@; exit 1; } close DBG; #------#

...and that also works just fine under the commandline, but bombs when invoked by the 3rd party program. This time, the error message in debug2.log is: Couldn't dup stdout! (Permission denied) at d:/users/jdoe/test9.pl line 12.

I'm trying hard to avoid invoking the shell. The scripts need to run under a wide variety of systems: ActiveState Perl, Cygwin Perl, actual Unix systems, ActiveState Perl running under Cygwin, etc. This makes using backquotes a real pain since it's hard to know what shell will be executed and thus what rules to use when shell escaping a command.

UPDATE: Forgot to actually ask the proper question: The 3rd party program seems to be doing something funky to stdout (and maybe stderr and/or stdin) which is causing open3 to bomb. Does anyone have any suggestions on how to cope with this, or a different way to tackle the problem without invoking the shell? I'd also appreciate any insight as to what could be going on, even if you don't have suggestions for a solution. I couldn't find anything on google or perlmonks' search, though maybe my search-fu is weak.

UPDATE 2: Running perl --version yields:

This is perl, v5.8.9 built for MSWin32-x86-multi-thread (with 13 registered patches, see perl -V for more detail) Copyright 1987-2008, Larry Wall Binary build 828 [294165] provided by ActiveState http://www.ActiveSta +te.com Built Dec 8 2010 16:51:11 Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge.

Replies are listed 'Best First'.
Re: IPC::Open3 Fails Under Win32 When Perl Is Called By Another Program
by SirRobin (Initiate) on Aug 05, 2013 at 19:31 UTC

    PS: I took a look at IPC::Open3.pm, and as far as I can tell it seems to be caused by the line $fd->{tmp_copy} = IO::Handle->new_from_fd($fd->{handle}, $fd->{mode}); failing, where $fd->{handle} is stdout. I made a few changes to IPC::Open3.pm to illustrate. The complete code for IPC::Open3.pm is below, with a comment marking all 3 changes:

Re: IPC::Open3 Fails Under Win32 When Perl Is Called By Another Program
by Anonymous Monk on Aug 06, 2013 at 00:14 UTC