Why is the STDOUT line printed after the STDERR line? - perl

I completly don't understand this behaviour.
I have a very simple Perl script:
#!/usr/bin/perl
use strict;
use warnings;
print "line 1\n";
print STDERR "line 2\n";
If I run it from console I will get what I expect:
$ perl a.pl
line 1
line 2
But if I redirect it to file, I will get the lines in the reverse order:
$ perl a.pl &> a
$ cat a
line 2
line 1
How can I capture all the outputs STDOUT+STDERR to the file with the same
order I get in the console?

This is an effect of buffering. When outputting to a terminal, Perl (just as stdio) uses line based buffering, which will give the expected effect. When outputting to a file, it uses block buffering (typically 4k or 8k blocks), hence it will only flush on program end.
The difference you're observing is because STDERR is unbuffered, unlike most other handles.
The easiest way to circumvent this issue is to enable autoflushing on STDOUT, using the $| special variable. See perlvar for more details.

Don't use $|, use autoflush instead.
STDOUT->autoflush(1);
STDERR->autoflush(1);
See http://perldoc.perl.org/IO/Handle.html

Related

Output correct line number for Perl script wrapped in batch on Windows?

Is there any specifier to tell Perl interpreter that the real Perl scripts starts at some line, then the output will have correct line number from interpreter, like interpret error message? For example, the line number information in below script is incorrect.
#perl -x "%~f0" %*
#exit /b %errorlevel%
#!perl
use strict;
printxxx "Perl Script\n"; # interpreter will output error for this line with **incorrect line number**
You can use the line directive, which is a special form of comment that looks like # line 99 and dictates the line number that perl will assign to the following line.
For instance
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
STDOUT->autoflush;
# line 100
say "line ". __LINE__;
die;
output
line 100
Died at E:\Perl\source\line.pl line 101.
It is also possible to add a filename after the line number, which similarly dictates the source file name that perl will report.

Perl STDERR printed in the wrong order with Tee

I'm trying to redirect STDOUT and STDERR from a perl script - executed from a bash script - to both screen and log file.
perlscript.pl
#!/usr/bin/perl
print "This is a standard output";
print "This is a second standard output";
print STDERR "This is an error";
bashscript.sh
#!/bin/bash
./perlscript.pl 2>&1 | tee -a logfile.log
If I execute the perlscript directly the screen output is printed in the correct order :
This is a standard output
This is a second standard output
This is an error
But when I execute the bash script the STDERR is printed first (in both screen and file) :
This is an error
This is a standard output
This is a second standard output
With a bash script as child the output is ordered flawlessly. Is it a bug with perl or tee? Am I doing something wrong?
An usual trick to turnoff buffering is to set the variable $|. Add the below line at
beginning of your script.
$| = 1;
This would turn the buffering off. Also refer to this excellent article by MJD explaining buffering in perl. Suffering from Buffering?
I guess this has to do with the way STDOUT and STDERR buffers are flushed. Try
autoflush STDOUT 1;
at the beginning of your perl script so that STDOUT is flushed after each print statement.

Print: producing no output

I'm fairly confused. I just got a new development machine, and perl appears to be outputting nothing for print commands.
#!/usr/bin/perl
use warnings;
use strict;
print "A";
print STDOUT "B";
print STDERR "C";
open FH, ">", "testprint';
print FH "D";
close FH;
Produces nothing in the console, and testprint becomes a 1-bye (empty) file.
Even this produces nothing:
perl -e "print 'a';"
This occurs for both perl binaries that happen to be on my machine. I'm stumped about where to start debugging this problem. Any ideas?
EDIT:
perl -v
This is perl, v5.8.8 built for x86_64-linx-thread-multi
and
which perl
/usr/bin/perl
I believe the problem exists outside of Perl. Either
the terminal in some unusual state when you ran the script,
perl's parent process redirected perl's output away from the terminal, or
perl's parent process did not provide a STDOUT and STDERR for perl.
You might be able to gather more information by actually checking if print returned an error. (It always baffles me why people don't check for errors when something doesn't work they way they expect it to work.)
perl -we'print("a") or die("Can'\''t print: $!\n");'
You might be able to gather more information by using strace or whatever it's called on your system. (Look for write(1 and write(2.)
strace perl -we'print("a") or die("Can'\''t print: $!\n");'
But those should print nothing at all if the problem is outside of Perl, which is why it might be wise to try redirecting the output to a file and then examining the file and its size.
perl -we'print("a") or die("Can'\''t print: $!\n");' 1>out 2>err
The problem was not STDOUT missing or redirected from the shell, but rather that the shell was set to send a carriage return without a newline when writing a prompt, thus overwriting all output sent to the same line.
Specifically, my old version of zsh had promptcr set. See question 3.23 here for more information.

command output is directed to the screen

novice Perl programmer here!.
I'm using the system() function to get the return code for an external program (in this case - php), however, the command's output is still printed to the screen.
How do I prevent it from doing so?
My code is:
use strict; use warnings;
print 'Return code:', system('php -l wrong.php'), "\n";
This code does print the return code, but it also print the the output of the command executed.
Any help will be appreciated!
EDIT: further testing shown that this happens while only using the php lint command.. using it with other commands doesnt print anything...
What you want is IPC::Open3:
use IPC::Open3;
use Symbol qw(gensym);
my $err = gensym; #work around IPC::Open3's interface
my $pid = open3 my $wtr, my $rdr, $err,
'some cmd', 'arg1', 'arg2', ...;
The separates out stdin into $wtr, stdout into $rdr, and stderr into $err
This also gives you maximum control over communicating with the process
If you are on a UNIX-like OS, you should be able to redirect the output in the command:
Try:
system('php -l wrong.php >> /dev/null') to get rid of what's being sent to stdout.
You could also open the command with a pipe to handle the output directly, which should be more portable.

How can I print just a unix newline in Perl on Win32?

By default, perl prints \r\n in a win32 environment. How can I override this? I'm using perl to make some changes to some source code in a repository, and I don't want to change all the newline characters.
I tried changing the output record separator but with no luck.
Thanks!
Edit: Wanted to include a code sample - I'm doing a search and replace over some files that follow a relatively straightforward pattern like this:
#!/usr/bin/perl
# test.pl
use strict;
use warnings;
$/ = undef;
$\ = "\n";
$^I=".old~";
while (<>) {
while (s/hello/world/) {
}
print;
}
This should replace any instances of "hello" with "world" for any files passed on the cmd line.
Edit 2: I tried the binmode as suggested without any luck initially. I delved a bit more and found that $^I (the inplace edit special variable) was overriding binmode. Any work around to still be able to use the inplace edit?
Edit 3: As Sinan points out below, I needed to use binmode ARGVOUT with $^I instead of binmode STDOUT in my example. Thanks.
Printing "\n" to a filehandle on Windows emits, by default, a CARRIAGE RETURN ("\015") followed by a LINE FEED ("\012") character because that the standard newline sequence on Windows.
This happens transparently, so you need to override it for the special filehandle ARGVOUT (see perldoc perlvar):
#!/usr/bin/perl -i.bak
use strict; use warnings;
local ($\, $/);
while (<>) {
binmode ARGVOUT;
print;
}
Output:
C:\Temp> xxd test.txt
0000000: 7465 7374 0d0a 0d0a test....
C:\Temp> h test.txt
C:\Temp> xxd test.txt
0000000: 7465 7374 0a0a test..
See also perldoc open, perldoc binmode and perldoc perliol (thanks daotoad).
Does binmode( STDOUT ) work?
Re: your question about the binmode being lost when $^I opens a new output handle, you could solve this with the open pragma:
use open OUT => ':raw';
which will force all filehandles opened for writing to have the ':raw' PerlIO layer (equivalent to binmode with no argument) to apply to them. Just take care if you're opening anything else for output that you apply :crlf or any other layer as needed.
The data you are reading in contains line endings, so you're getting them back out again. You can strip them off yourself with chomp, then add your own ending back, provided you have set binmode as Sinan describes::
while (<>) {
binmode;
chomp; # strip off \r\n
while (s/search/replace/) {
# ...
}
print;
print "\n"; # add your own line ending back
}
By default, perl prints \r\n in a win32 environment. How can I override this?
I ended up creating my own file and setting binmode(fh) specifically. I could not get STDOUT (or ARGVOUT) to work reliably under both Windows 10 using perl 5.8.8 and Windows 7 with perl 5.14.4.
perl -e 'open(fh, ">x"); binmode(fh); print fh "\n";' ; od -c x
0000000 \n
Sometimes the binmode(fh) was needed here and sometimes it seemed to be the default.
I could not get binmode(STDOUT) to be work reliably. Some of the following did output just \n under Windows:
perl -e 'binmode(ARGVOUT); print "\n";' | od -c
perl -e 'binmode(STDOUT); print "\n";' | od -c
perl -e 'binmode(STDOUT); syswrite(STDOUT, "\n");' | od -c
... but then not when the output was going to a file. The following still spat out \r \n.
perl -e 'binmode(STDOUT); print "\n";' > x ; od -c x
perl -e 'binmode(ARGVOUT); print "\n";' > x ; od -c x
Interestingly, the following worked when piping to cat which then writes to a file. Perl must be seeing if STDOUT is a terminal, file, or pipe and enabling the cr-lf layer or not. Why a pipe is binary but a file is not is an interesting decision. There are also differences between running perl interactively from the command-line and running it from a script with the same args and redirects.
perl -e 'binmode(STDOUT); print "\n";' | cat > x ; od -c x
Noticed that I tried print and syswrite. I was surprised that syswrite didn't give me a direct layer to the file-handle. I also tried to copy the STDOUT file-handle and set binmode on that new file-handle but that didn't work either. PERLIO environmental variable didn't help either. The use out => ":raw"; worked under Windows 10 perl 5.8.8 but not Windows 7 perl 5.14.4 when redirected to an output file.
Btw, I wasn't doing a print "\n"; in my code when I stumbled over this problem. I was doing a print of pack("c", $num); where $num happened to be 10. Imagine my surprise when my binary file was corrupted by \rs.
Porting sucks!
A unix newline is a LINEFEED character, which is ASCII code 10.
print "\012";