Perl's print function: print to last filehandle? - perl

I am confused by this bit of Perldoc:
If FILEHANDLE is omitted, prints to the last selected (see select) output handle.
http://perldoc.perl.org/functions/print.html
It seems to say that a naked print statement, after writing to a filehandle, will print to that filehandle. I wrote a script to test this...
#!/usr/bin/perl
open (FILE, '>', 'PrintTest.txt') or die $!;
print FILE "Hello world!\n";
print "Hello.... hello? hello world!\n";
close FILE;
But the test shows otherwise.
$ perl PrintTest.pl
Hello.... hello? hello world!
We're writing to STDOUT here, not FILE, which is probably the most sensible result, but seems contrary to that line of Perldoc quoted above. Perhaps I am misunderstanding what "last selected output handle" means? That is the only way I can think to explain this :-p
Thanks in advance ~ ktm

It seems to say that a naked print statement, after writing to a filehandle, will print to that filehandle.
No, it says it will print to the last selected handle, not the last handle to which you printed. It proceeds to instruct you to read this page to see how to do it.
open (FILE, '>', 'PrintTest.txt') or die $!;
print FILE "Hello world!\n";
select(FILE); <----- Missing
print "Hello.... hello? hello world!\n";
close FILE;

I believe a SELECT statement is required to change the default handle from stdout to another. Then, the selected handle remains in use until changed.

Related

Print into file descriptor using perl s/.../.../e

I am encountering an error with the following perl code:
#!/usr/bin/perl
open $fh, '>', 'myfile.txt' or die;
s|(.*)|print $fh "Hello",$1," how are you"|e;
close $fh or die
String found where operator expected at ./script.pl line 3, near "$fh "Hello""
(Missing operator before "Hello "?)
I use the s|(.*)|...|e to write the start of the string another time, like Hello Hello.
The expected output the file myfile.txt is
Hello Hello, how are you?
Is there a way to make it run with the actual operations?
Or must I change the s|(.*)|...|e operation in another way?
Interestingly, what works is to enclose the filehandle in curly brackets:
s/(.*)/print {$fh} "Hello",$1," how are you"/e;
It's a good practice to use them always, anyway.

STDOUT to array Perl

I am compiling a Perl program, i am writing the output STDOUT to a file. In the same program , i want to run another small script using while function on the output of STDOUT. So, I need to save the output of first script in an array, then i can use in while<#array>. Like
open(File,"text.txt");
open(STDOUT,">output,txt");
#file_contents=<FILE>;
foreach (#file_contents){
//SCRIPT GOES HERE//
write;
}
format STDOUT =
VARIABLE #<<<<<< #<<<<<< #<<<<<<
$x $y $z
.
//Here I want to use output of above program in while loop //
while(<>){
}
How can i save the output of first program into array so that i can use in while loop, or how can i directly use STDOUT in while loop. I have to make sure that first part is completely executed. Thanks in advance.
Since you remapped STDOUT so it writes to a file, you could presumably close STDOUT, and then reopen the file for reading.
Quite where you're going to send any other output is a bit of a mystery, but presumably you can resolve that. Were it me, I'd not fiddle with STDOUT. I'd make the script write to a file handle:
use strict;
use warnings;
open my $input, "<", "text.txt" or die "A horrible death";
open my $output, ">", "output.txt" or die "A horrible death";
my #file_contents = <$input>;
close($input);
foreach (#file_contents)
{
# Script goes here
print $output "Any information that goes to output\n";
}
close $output;
open my $reread, "<", "output.txt" or die "A horrible death";
while (<$reread>)
{
# Process the previous output
}
Note the use of lexical file handles, the checking that the open worked, the close when finished with the input file, the use of use strict; and use warnings;. (I've only been working with Perl for 20 years and I know I don't trust my scripts until they run clean with those settings.)
I assume you want to reopen STDOUT in order to make the write function work. However, the correct solution for that is to either specify the file handle, or to a lesser extent, to use select.
write FILEHANDLE;
or
select FILEHANDLE;
write;
Unfortunately, it seems the IO of perlform is a bit arcane, and does not seem to allow for lexical file handles.
Your problem is you can't reuse the formatted text within the program, so a bit of trixy programming is required. What you can do is open a file handle that prints to a scalar. Which is another somewhat arcane perl functionality, but in this case, it might be the only way to do this directly.
# Using FOO as format to avoid destroying STDOUT
format FOO =
VARIABLE #<<<<<< #<<<<<< #<<<<<<
$x $y $z
.
my $foo;
use autodie; # save yourself some typing
open INPUT, '<', "text.txt"; # normally, we would add "or die $!" on these
open FOO, '>', \$foo; # but now autodie handles that for us
open my $output, '>', "output.txt";
while (<FILE>) {
$foo = ""; # we need to reset $foo each iteration
write FOO; # write to the file handle instead
print $output $foo; # this now prints $foo to output.txt
do_something($foo); # now you can also process the text at the same time
}
As you'll notice, we now first print the formatted line to the scalar $foo. While it is there, we can handle it as regular data, so there's no need to save to a file and reopening it to get to the data.
Each iteration, data is concatenated to the end of $foo, so to avoid accumulation, we need to reset $foo. The best way to handle this would be to make $foo lexical within the scope, but unfortunately we need $foo to be declared outside the while loop in order to be able to use it in the open statement.
It might be possible to use local $foo inside the while-loop, but I think that's adding yet more bad practice to this already very bad hack.
Conclusion:
With all this said and done, I suspect the best way to handle this is to not use perlform at all, and format your data in some other way. While perlform might be well suited to print to a file, it is not the best suited for what you have in mind. I recall this question from earlier, perhaps there was some other answer that would work better. Such as using sprintf, like Jonathan suggested
Assuming the output from your first program is tab-delimited:
while (<>) {
chomp $_;
my ($variable, $x, $y, $z) = split("\t", $_);
# do stuff with values
}

Redirecting STDOUT of a pipe in Perl

I feel as though there should be a simple way to do this, but searching around gives me no good leads. I just want to open() a pipe to an application, write some data to it, and have the output of the subprocess sent to the STDOUT of the calling script.
open(my $foo, '|-', '/path/to/foo');
print $foo 'input'; # Should behave equivalently to "print 'output'"
close($foo);
Is there a simple way to do this, or have I hit upon one of the many "can't get there from here" moments in Perl?
The subprocess will inherit STDOUT automatically. This works for me:
open(my $f, "|-", "cat");
print $f "hi\n";
If you are not really closing the pipe immediately the problem might be on the other end: STDOUT is line-buffered by default, so you see print "hello world\n" immediately. The pipe to your subprocess will be block-buffered by default, so you may actually be waiting for the data from your perl script to reach the other program:
open(my $f, "|-", "cat");
print $f "hi\n";
sleep(10);
close($f); # or exit
# now output appears
Try adding select $f; $| = 1 (or I think the more modern way is $f->autoflush(1))

How can I redirect standard output to a file in Perl?

I'm looking for an example of redirecting stdout to a file using Perl. I'm doing a fairly straightforward fork/exec tool, and I want to redirect the child's output to a file instead of the parents stdout.
Is there an equivilant of dup2() I should use? I can't seem to find it
From perldoc -f open:
open STDOUT, '>', "foo.out"
The docs are your friend...
As JS Bangs said, an easy way to redirect output is to use the 'select' statement.
Many thanks to stackoverflow and their users. I hope this is helpful
for example:
print "to console\n";
open OUTPUT, '>', "foo.txt" or die "Can't create filehandle: $!";
select OUTPUT; $| = 1; # make unbuffered
print "to file\n";
print OUTPUT "also to file\n";
print STDOUT "to console\n";
# close current output file
close(OUTPUT);
# reset stdout to be the default file handle
select STDOUT;
print "to console";
The child itself can do select $filehandle to specify that all of its print calls should be directed to a specific filehandle.
The best the parent can do is use system or exec or something of the sort to do shell redirection.
open my $fh, '>', $file;
defined(my $pid = fork) or die "fork: $!";
if (!$pid) {
open STDOUT, '>&', $fh;
# do whatever you want
...
exit;
}
waitpid $pid, 0;
print $? == 0 ? "ok\n" : "nok\n";
A strictly informational but impractical answer:
Though there's almost certainly a more elegant way of going about this depending on the exact details of what you're trying to do, if you absolutely must have dup2(), its Perl equivalent is present in the POSIX module. However, in this case you're dealing with actual file descriptors and not Perl filehandles, and correspondingly you're restricted to using the other provided functions in the POSIX module, all of which are analogous to what you would be using in C. To some extent, you would be writing C in very un-Perlish Perl.
http://perldoc.perl.org/POSIX.html

Perl open file problem

I am having some trouble trying to print from a file. Any ideas? Thanks
open(STDOUT,">/home/int420_101a05/shttpd/htdocs/receipt.html");
#Results of a sub-routine
&printReceipt;
close(STDOUT);
open(INF,"/home/int420_101a05/shttpd/htdocs/receipt.html"); $emailBody = <INF>;
close(INF);
print $emailBody;
ERRORS: Filehandle STDOUT reopened as INF only for input at ./test.c line 6.
print() on closed filehandle STDOUT at ./test.c line 9.
This discussion addresses the technical reason for the message. Relevant info from the thread is this:
From open(2) manpage:
When the call is successful, the file descriptor returned will be
the lowest file descriptor not currently open for the process.
But STDOUT still refers to the
filehandle #1. This warning could be
useful. Although one can argue that
further uses of STDOUT as an output
filehandle will trigger a warning as
well...
So, to summarize, you closed STDOUT (file descriptor 1) and your file will be open as FD#1. That's due to open()'s properties.
As other have noted, the real reason you're having this problem is that you should not use STDOUT for printing to a file unless there's some special case where it's required.
Instead, open a file for writing using a new file handle:
open(OUTFILE,">/home/int420_101a05/shttpd/htdocs/receipt.html")
|| die "Could not open: $!";
print OUTFILE "data";
close(OUTFILE);
To print to filehandle from subroutine, just pass the file handle as a parameter.
The best way of doing so is to create an IO::File object and pass that object around
my $filehandle = IO::File->new(">$filename") || die "error: $!";
mySub($filehandle);
sub mySub {
my $fh = shift;
print $fh "stuff" || die "could not print $!";
}
You can also set a particular filehandle as a default filehandle to have print print to that by default using select but that is a LOT more fragile and should be avoidded in favor of IO::File solution.
If you want to temporarily change the standard output, use the select builtin. Another option is to localize the typeglob first:
{
local *STDOUT;
open STDOUT, '>', 'outfile.txt' or die $!;
print "Sent to file\n";
}
Don't try to open the STDOUT handle. If you want to print to STDOUT, just use print (with no filehandle argument). If you want to print to something other than STDOUT, use a different name.