Redirecting STDOUT to Scalar behaves not as expected. Why?

Z

zaphod

Hi,

to explain what I want to do, I made to demos:

This is the first version, which does exactly what I want to do except,
that it writes STDOUT to a file:

################################################################################
## demo1.pl
################################################################################
use IO::File;

# My Little Programm
my @PROGRAMM = (
"# This is a demo-Programm\n",
"print \"Hello World!\\n\";\n",
"for my \$i (1..5){\n",
" print \"\$i\\n\";\n",
"}\n"
);


# Saving STDOUT
open(OLDOUT,">&STDOUT") || die("[$PROGNAME] Couldn't dup STDOUT\n");


# Redirecting STDOUT to file
close(STDOUT);
die("Couldn't write perl.out\n")
unless open(STDOUT,">perl.out");


# Open Pipe to Perl
my $perl = new IO::File;
$perl->open("|perl");


# Run Programm
for my $code (@PROGRAMM){
print {$perl} $code;
}

# Restore STDOUT
close(STDOUT);
open(STDOUT,">&OLDOUT");

print "Hello again!\n";

################################################################################


now I try to redirect the output to a scalar variable, which works for the
simple print statement, but not for the output of the perl-interpreter
where the programm is piped to:


################################################################################
## demo2.pl
################################################################################
use IO::File;
use IO::Scalar;

# My Little Programm
my @PROGRAMM = (
"# This is a demo-Programm\n",
"print \"Hello World!\\n\";\n",
"for my \$i (1..5){\n",
" print \"\$i\\n\";\n",
"}\n"
);


# Saving STDOUT
open(OLDOUT,">&STDOUT") || die("[$PROGNAME] Couldn't dup STDOUT\n");


# Redirecting STDOUT to Scalar
my $scalar = "";
close(STDOUT);
tie *STDOUT, 'IO::Scalar', \$scalar;

# Show that STDOUT to Scalar works somehow:
print "Hi";

# Open Pipe to Perl
my $perl = new IO::File;
$perl->open("|perl");


# Run Programm
for my $code (@PROGRAMM){
print {$perl} $code;
}



# Restore STDOUT
close(STDOUT);
untie *STDOUT;
open(STDOUT,">&OLDOUT");

print "Hello again!\n";
print ">>>$scalar<<<\n";

################################################################################


What I do not understand is, why there is a different behaviour in the two
versions of the programm and what I can do to capture the result of piping
the programm to perl into $scalar.

Hope there is somebody out there, who can help.


Regards
Thomas

PS.: I know about the existance of "eval", so please no comments about it.
 
M

Michele Dondi

This is the first version, which does exactly what I want to do except,
that it writes STDOUT to a file: [snip]
use IO::File;

If you like the OO UI of IO::File better, then fine. But for your
purposes recent enough perls support lexical FHs out of the box, so
IMHO no need to use it.
# My Little Programm
my @PROGRAMM = (
"# This is a demo-Programm\n",
"print \"Hello World!\\n\";\n",
"for my \$i (1..5){\n",
" print \"\$i\\n\";\n",
"}\n"
);

As a side note, what about a HERE doc (or DATA section)?
# Saving STDOUT
open(OLDOUT,">&STDOUT") || die("[$PROGNAME] Couldn't dup STDOUT\n");
^^^^^^^^^
^^^^^^^^^

Is this a real *working* minimal example? I don't think so... well, on
a second thought I do think so, since we're not under warnings and
strictures. But no real harm done... so please do not take any offence
for this comment!
# Redirecting STDOUT to file [snip]
# Open Pipe to Perl [snip]
# Run Programm [snip]
# Restore STDOUT

As a side note, I hope I'm not missing anything obvious, but aren't
the "Redirecting STDOUT to file then Restore STDOUT" and "Open Pipe to
Perl then Run Programm" businesses orthogonal to each other?!? So what
has opening a pipe to perl to do with your redirecting STDOUT
concerns?
now I try to redirect the output to a scalar variable, which works for the
simple print statement, but not for the output of the perl-interpreter
where the programm is piped to: [snip]
use IO::File;
use IO::Scalar;

No need for IO::Scalar either, for recent enough perls support
open()ing scalar references "in memory" straight out of the box.

All in all see if the following suits your needs:


#!/usr/bin/perl -l

use strict;
use warnings;

open my $oldout, '>&STDOUT' or
die "$0: Couldn't dup STDOUT\n";

close STDOUT;

my $scalar;
open STDOUT, '>', \$scalar or
die "$0: Couldn't open STDOUT in memory\n";

print 'Hi';

{
open my $perl, '|-', 'perl' or
die "$0: Couldn't pipe into perl\n";

print $perl <<'EOPERL';
# This is a demo-Programm
print "Hello World!\n";
for my $i (1..5) {
print "$i\n";
}
EOPERL
}

close STDOUT;
open STDOUT, '>&', $oldout or
die "$0: Couldn't restore STDOUT\n";

print 'Hello again!';
print '$scalar: ', $scalar;

__END__

What I do not understand is, why there is a different behaviour in the two
versions of the programm and what I can do to capture the result of piping
the programm to perl into $scalar.

Sorry, I can't answer to that for it was easier for me to rewrite the
program from scratch.
PS.: I know about the existance of "eval", so please no comments about it.

I don't think I've made any! ;-)

Last, isn't it that you just need to know about select()? See this
example:

#!/usr/bin/perl -l

use strict;
use warnings;

my $scalar;
open my $fh, '>', \$scalar or
die "$0: Couldn't open STDOUT in memory\n";

my $oldout=select $fh;
print 'Hi';
select $oldout;
print '$scalar: ', $scalar;

__END__


HTH,
Michele
 
Z

zaphod

On Sat, 27 Nov 2004 11:05:19 +0100, Michele Dondi wrote:

Hi Michele,

Thank you for your short tutorial, but I think you have not got the point.
I was not asking about optimizing my code for a recent perl version, but
why I can redirect the output of the piped perl program into a file but
not into a string. I am sorry, when I was not saying this clear enough.
This is the first version, which does exactly what I want to do except,
that it writes STDOUT to a file: [snip]
use IO::File;

If you like the OO UI of IO::File better, then fine. But for your
purposes recent enough perls support lexical FHs out of the box, so
IMHO no need to use it.

How recent? I am using perl 5.6, the code I started this script with came
originally from perl 5.0.
As a side note, what about a HERE doc (or DATA section)?

I am sorry, I did not put "I know about HERE doc" in P.S. ;-)
I was using an array, because I wanted to print the code line by line
into |perl, this is close to the behaviour of the 'real' program. In the
'real' programm there will be no embeded code.
# Saving STDOUT
open(OLDOUT,">&STDOUT") || die("[$PROGNAME] Couldn't dup STDOUT\n");
^^^^^^^^^
^^^^^^^^^

Is this a real *working* minimal example? I don't think so... well, on
a second thought I do think so, since we're not under warnings and
strictures. But no real harm done... so please do not take any offence
for this comment!

Sorry about that - I missed to change this when putting the sample code
together.
# Redirecting STDOUT to file [snip]
# Open Pipe to Perl [snip]
# Run Programm [snip]
# Restore STDOUT

As a side note, I hope I'm not missing anything obvious, but aren't
the "Redirecting STDOUT to file then Restore STDOUT" and "Open Pipe to
Perl then Run Programm" businesses orthogonal to each other?!? So what
has opening a pipe to perl to do with your redirecting STDOUT
concerns?

This is the most interesting part of the code: the piped perl also
produces output to STDOUT. In demo2.pl I do not find it in the $scalar,
while in demo1.pl (which uses a file instead of $scalar) it goes into the
file as wanted.
now I try to redirect the output to a scalar variable, which works for the
simple print statement, but not for the output of the perl-interpreter
where the programm is piped to: [snip]
use IO::File;
use IO::Scalar;
No need for IO::Scalar either, for recent enough perls support
open()ing scalar references "in memory" straight out of the box.

All in all see if the following suits your needs:


#!/usr/bin/perl -l

use strict;
use warnings;

open my $oldout, '>&STDOUT' or
die "$0: Couldn't dup STDOUT\n";

close STDOUT;

my $scalar;
open STDOUT, '>', \$scalar or
die "$0: Couldn't open STDOUT in memory\n";

print 'Hi';

{
open my $perl, '|-', 'perl' or
die "$0: Couldn't pipe into perl\n";

print $perl <<'EOPERL';
# This is a demo-Programm
print "Hello World!\n";
for my $i (1..5) {
print "$i\n";
}
EOPERL
}

close STDOUT;
open STDOUT, '>&', $oldout or
die "$0: Couldn't restore STDOUT\n";

print 'Hello again!';
print '$scalar: ', $scalar;

__END__



Sorry, I can't answer to that for it was easier for me to rewrite the
program from scratch

But did you notice, that it does not work? Under perl 5.6 it will not run
at all, but with perl 5.8 it does.
So when I try your code I get:

--------------------------
Hello again!
$scalar: Hi
--------------------------

instead of

--------------------------
Hello again!
$scalar: HiHello World!
1
2
3
4
5
--------------------------

So I thought, this might be a Win32-issue, but on linux I get the same
results. Thats why I framed $scalar in >>> <<<.
I don't think I've made any! ;-)

But a lot of others ;-)
Last, isn't it that you just need to know about select()? See this
example:

#!/usr/bin/perl -l

use strict;
use warnings;

my $scalar;
open my $fh, '>', \$scalar or
die "$0: Couldn't open STDOUT in memory\n";

my $oldout=select $fh;
print 'Hi';
select $oldout;
print '$scalar: ', $scalar;

__END__

A nice other example for redirecting to a string, but not more. I added
the |perl-part and got another result. Like in your example before 'Hi'
goes to $scalar, but while the output of |perl is supressed, here it still
goes to the terminal as if STDOUT has not been redirected.

Thanks anyway, now I know a little more about perl 5.8.

Regards
Thomas
 
M

Michele Dondi

open my $fh, '>', \$scalar or
die "$0: Couldn't open STDOUT in memory\n";

[ashamed]

Of course, even if these were meant to be *minimal* examples, I forgot
including in ordieish statements $!, which would have been a Very Good
Thing(TM) anyway.

Also, *this* message is misleading (and incorrect!): it was left over
from the first script that I quickly edited to obtain the second one.


Sorry,
Michele
 
M

Michele Dondi

On Sat, 27 Nov 2004 11:05:19 +0100, Michele Dondi wrote:

Hi Michele,

Thank you for your short tutorial, but I think you have not got the point.
I was not asking about optimizing my code for a recent perl version, but
why I can redirect the output of the piped perl program into a file but
not into a string. I am sorry, when I was not saying this clear enough.

Sorry then! The answer is that with standard Perl functions/operators
you can't both pipe *into* a program and *out* of it. Rumors have it
that File::Open{2,3} supply this kind of facility. I don't think that
the other modules you were using "do that kinda things"...
How recent? I am using perl 5.6, the code I started this script with came
originally from perl 5.0.

5.6 should do it!
I am sorry, I did not put "I know about HERE doc" in P.S. ;-)
I was using an array, because I wanted to print the code line by line
into |perl, this is close to the behaviour of the 'real' program. In the
'real' programm there will be no embeded code.

Using an HERE doc or anything more readable/maintainable than an array
of strings full of "\n"s would be just as close to it. This is the
point I wanted to stress...
This is the most interesting part of the code: the piped perl also
produces output to STDOUT. In demo2.pl I do not find it in the $scalar,

Ahhhhh!!!

That's the point: but it produces output to *its own* STDOUT which has
"nothing" to do with the main script's one. Then see the cmt at the
beginning.
while in demo1.pl (which uses a file instead of $scalar) it goes into the
file as wanted.

I've not checked, but personally I would think of *that* as the
*wrong* behaviour. This is why I didn't even understand what you were
after in the first place. To make things clear: there's no (or there
should not be) such a thing like a "system-wide STDOUT".
All in all see if the following suits your needs: [snip code]
Sorry, I can't answer to that for it was easier for me to rewrite the
program from scratch

But did you notice, that it does not work? Under perl 5.6 it will not run
at all, but with perl 5.8 it does.

Sorry, I've begun with 5.6.1, and I haven't noticed any macroscopic
change up to 5.8.*. I must admit I haven't the slightest idea why it
doesn't work for 5.6, sorry!
So when I try your code I get:

But this is what I expected. And I didn't expect you were expecting
anything different. See the cmt at the beginning again!
Last, isn't it that you just need to know about select()? See this
example:
[snip code]
A nice other example for redirecting to a string, but not more. I added
the |perl-part and got another result. Like in your example before 'Hi'
goes to $scalar, but while the output of |perl is supressed, here it still
goes to the terminal as if STDOUT has not been redirected.

Again, it does what it is supposed to do, that is what I *thought* you
were after anyway: it redirects the script's STDOUT. Of course it
doesn't redirect the output of the forked perl process. And as far as
I'm concerned I don't see any reason why it should!


Michele
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top