http://qs321.pair.com?node_id=390317

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

Is is possible to tie STDIN/STDOUT using IO::Scalar such that an mini-program run via an eval statement would, when using STDIN and STDOUT, actually be reading and writing strings? This snippet gives the sense of what I'm trying to do:

# untested my $code = 'while (<>) { print; }'; # would typically be more interest +ing my $output = ''; # storage for output my $input = "more and more\nstuff here"; # would typically be more int +eresting { tie *STDIN, 'IO::Scalar', \$input; tie *STDOUT, 'IO::Scalar', \$output; eval "$code"; } # interesting stuff should be in output here
My program seems to hang at the  while(<>). Thanks for advice, great monks!

water

Replies are listed 'Best First'.
Re: tieing STDIN & STDOUT using IO::Scalar for use inside an eval
by ikegami (Patriarch) on Sep 11, 2004 at 21:41 UTC

    I don't have IO::Scalar installed, so I can't help you with that module. However, you can do it without IO::Scalar in 5.8.0:

    # This \$var syntax of open() requires Perl 5.8.0 or higher. use 5.8.0; my $input = "test\nfoo\nbar\n"; my $output = ''; my $code = 'print while (<STDIN>)'; { local *STDIN; open(STDIN, '<', \$input) or die("Can't open string for reading.\n"); local *STDOUT; open(STDOUT, '>', \$output) or die("Can't open string for writing.\n"); eval "$code"; } print("\$output contains:\n$output"); __END__ output ====== $output contains: test foo bar

    A more general example:

    # This \$var syntax of open() requires Perl 5.8.0 or higher. use 5.8.0; my $input = "test\nfoo\nbar\n"; open(my $old_stdin, "<&STDIN") or die("Can't dup STDIN: $!.\n"); close(STDIN); open(STDIN, '<', \$input) or die("Can't open string for reading\n"); my $output = ""; open(my $old_stdout, ">&STDOUT") or die("Can't dup STDOUT: $!.\n"); close(STDOUT); open(STDOUT, '>', \$output) or die("Can't open string for writing\n"); print($_) while (<STDIN>); print $old_stdout ("\$output contains:\n$output"); __END__ output ====== $output contains: test foo bar
Re: tieing STDIN & STDOUT using IO::Scalar for use inside an eval
by water (Deacon) on Sep 11, 2004 at 21:45 UTC
    upon further examination, the issue hinges on  while(<>), which doesn't work in this context, versus while(<STDIN>), which does.

    So, problem solved.

    But, as I'm curious, can anyone explain why the magic <> doesn't work here?

    Happy Happy Happy water

      But, as I'm curious, can anyone explain why the magic <> doesn't work here?
      Because <> is a synonym for <ARGV>, and ARGV is a magic filehandle that sometimes reads from STDIN, but sometimes reads from files etc.

      Dave.

        That's right... specifically, it depends on what is currently in @ARGV (if there's anything in @ARGV, the contents are treated as file names, and the files are magically opened and read from... if @ARGV is empty, it'll read from STDIN).

        If the intent is to read from STDIN, then do so, explicitly, by <STDIN>. If the intent is to process files, generically, either named files or files piped through, then use <>. In the case of the original post, which seems to be (maybe) testing some external code and trying to sandbox it, somewhat, it might just be a good idea to localize @ARGV (that is, to extend the sandbox beyond just STDIN and STDOUT (why isn't he also tieing STDERR, then, though?) to include the command line).

        ------------ :Wq Not an editor command: Wq