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

Hey Monks,

I'm in a bit of a crunch. I'm running a tool and redirecting the output from it to a file. That is working fine, but I'm having problems trying to read from that file. I tried closing it and then reopening it again as stated in perlfunc open
Though if you try to re-open "STDOUT" or "STDERR" as an "in memory" file, you have to close it first: close STDOUT; open STDOUT, '>', \$variable or die "Can't open STDOUT: $!";
but that isn't working for me. Could someone please explain what I'm doing wrong. Thanks.

use strict; use warnings; my $cmd = 'd:\clwhois.exe'; my $ip = '192.168.1.1'; my $file = 'd:\temp\output.txt'; open(STDOUT, ">$file") or die "Can't open $file: $!\n"; # redirect STD +OUT to a temp file system ("$cmd $ip") and warn "Can not execute $cmd: $!\n"; close STDOUT; open(STDOUT, "$file") or die "Can't open $file: $!\n"; while (<STDOUT>){ print; } close STDOUT;

Replies are listed 'Best First'.
Re: Reading from STDOUT after Redirecting it.
by chromatic (Archbishop) on Jul 02, 2003 at 22:18 UTC

    Do you need to write the file? If you just want the output, you could use backticks. (Of course, I've never done this on Windows, so you might have to use IPC::Open, in which case your approach is much faster in development time.)

    my @output = `$cmd $ip`;
      Definitely agree that backticks are the way to go. I have used them on Windows and I've never had a problem.
Re: Reading from STDOUT after Redirecting it.
by atnonis (Monk) on Jul 02, 2003 at 21:53 UTC
    Hi there!
    You can't use STDOUT as an input because as the name says it's only for output :) Instead open the file to an other FH (eg. FILE)
    use strict; use warnings; my $cmd = 'd:\clwhois.exe'; my $ip = '192.168.1.1'; my $file = 'd:\temp\output.txt'; open(STDOUT, ">$file") or die "Can't open $file: $!\n"; # redirect STD +OUT to a temp file system ("$cmd $ip") and warn "Can not execute $cmd: $!\n"; close STDOUT; open(FILE, "$file") or die "Can't open $file: $!\n"; while (<FILE>){ print; } close STDOUT;

    Hope that helps and if im mistaken please correct me

    atnonis!
      atnonis,

      Thanks for the suggestion. I tried this as well before posting and it does not work. I get the error message print() on closed filehandle STDOUT. I'm going to try the backticks like other's have suggested.

Re: Reading from STDOUT after Redirecting it.
by BrowserUk (Patriarch) on Jul 03, 2003 at 00:05 UTC

    It might be worth pointing out that re-opening STDOUT within your perl script will only affect stuff printed by that perl script and not the output from programs you spawn from within your perl script using system. They have their own set of file handles which are independant of those in your script.

    There are three main ways of capturing the output from a spawned command.

    1. my $output = `cmd args`;
    2. my $output = qx/cmd args/;
    3. open IN, "cmd args |" or die $!; while( <IN> ) { # do something with each line of input } close IN;

    The latter form can be convenient if your command produces a large volume of output but you only need a few or selected lines.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      It might be worth pointing out that re-opening STDOUT within your perl script will only affect stuff printed by that perl script and not the output from programs you spawn from within your perl script using system. They have their own set of file handles which are independant of those in your script.
      I don't think that's true; the child process should inherit all the file handles.

        Child processes inherit all file descriptors, but a file handle is an internal Perl thing and isn't inherited in the way you'd expect.

        When a new process starts up, standard in is on file descriptor 0, standard out on file descriptor 1, and standard error on file descriptor 2. To redirect one of these, a Perl script has to assign a new file to one of these file descriptors. But that's not what happens by just reassigning STDOUT:

        #!/usr/bin/perl open(STDOUT,"> /tmp/t26.out");
        $ strace -e open perl /tmp/t26
        ...
        open("/tmp/t26.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
        
        Fortunately, it's not that hard to work around; open always returns the lowest numbered file descriptor, so closing STDOUT right before re-opening it will do what you expect:
        #!/usr/bin/perl close(STDOUT); open(STDOUT,"> /tmp/t27.out");
        $ strace -e open perl /tmp/t27
        ...
        open("/tmp/t27.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
        
      One variation:
      open(CMD, "($cmd | sed 's/^/STDOUT:/') 2>&1 |"); print " test\n" if $DEBUG; while (<CMD>) { if (!s/^STDOUT://) { print " Command failed: ", $_; exit 2; } else { chomp; @argTest = split; } }
Re: Reading from STDOUT after Redirecting it.
by sgifford (Prior) on Jul 03, 2003 at 06:12 UTC
    The second-easiest way to do what you want (after backticks) is to just have the shell do the work:
    system("$cmd $ip >$file") and warn "Can not execute $cmd: $!\n";
Re: Reading from STDOUT after Redirecting it.
by bm (Hermit) on Jul 03, 2003 at 11:46 UTC

    <aside>

    When capturing output from external tools, I almost always capture STDERR as well.

    As was pointed out recently, this is straightforward in recent versions of Perl:

     my @output = `$cmd $ip 2>&1`;

    Perl strips the trailing redirection and redirects STDERR to STDOUT for you, and places both into the list. I believe this is also the case on Win32. Actually, I think this is the case irrespective of platform...

    </aside>