I am looking at some old code in Perl, where the author has writtern
$| = 1 in the first line.
But the code does not have any print statements, it calls a C++ binary using the system command. Now I read that $| will force flush after every print. So does it affect the system command's output in any way or am I safe to remove that line.
Thanks
Arvind
I do not believe so. The $| will affect the way that Perl is running, not any external executable.
You should be safe to remove it.
perldoc - perlvar : States "If set to nonzero, forces a flush right away and after every write or print on the currently selected output channel.". I think the important thing here is the "currently selected output channel". The external application will have it's own output channel.
With questions like this it is often easy to write a trivial program that shows what the behavior is:
#!/usr/bin/perl
use strict;
use warnings;
if (#ARGV) {
output();
exit;
}
print "in the first program without \$|:\n";
output();
$| = 1;
print "in the first program with \$|:\n";
output();
print "in system with \$|\n";
system($^X, $0, 1) == 0
or die "could not run '$^X $0 1' failed\n";
$| = 0;
print "in system without \$|\n";
system($^X, $0, 1) == 0
or die "could not run '$^X $0 1' failed\n";
sub output {
for my $i (1 .. 4) {
print $i;
sleep 1;
}
print "\n";
}
From this we can see that setting $| has no affect on programs run through system.
This is something that you can easily check yourself. Create a program where buffering matters, like printing a series of dots. You should see the output all at once after ten seconds since the output is buffered:
#!perl
foreach ( 1 .. 10 )
{
print ".";
sleep 1;
}
print "\n";
Now, try setting $| and calling this with system:
% perl -e "$|++; system( qq|$^X test.pl| )";
For my test case, the $| value didn't affect the buffering in the child process.
Related
I have a Perl script, which runs an external executable. That executable runs for a while (sometimes seconds, sometimes an hour), can spit out text to both STDOUT and STDERR as well as an exit code, which all are needed. Following code demonstrates first successful external executable run (small bash script with one line - the comment), then with bad exit status (example with gs - ghostscript).
I want the external executable give its STDOUT to the Perl script for evaluation, filtering, formatting etc. before it gets logged to a logfile (used for other stuff as well) while the external is still executing. STDERR would also be great to be worked on same way.
This script is in stand to log everything from STDOUT, but only after the executable has finished. And the STDERR is logged only directly, without evaluations etc. I have no possibility to install any additional Perl parts, modules etc.
How do I get my Perl script to get each line (STDOUT + STDERR) from the executable while it is spitting it out (not just at the end) as well as its exit code for other purposes?
#!/usr/bin/perl
#array_executable_and_parameters = "/home/username/perl/myexecutable.sh" ; #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
#array_executable_and_parameters2= "gs aaa" ;
my $line;
chdir("/home/username/perl/");
$logFileName = "logfileforsomespecificinput.log";
open(LOGHANDLE, ">>$logFileName" );
open (STDERR, '>>', $logFileName); #Prints to logfile directly
#open (STDERR, '>>', <STDOUT>); #Prints to own STDOUT (screen or mailfile)
print LOGHANDLE "--------------OK run\n";
open CMD, '-|', #array_executable_and_parameters or die $#;
while (defined($line = <CMD>)) { #Logs all at once at end
print LOGHANDLE "-----\$line=$line-----\n";
}
close CMD;
$returnCode1= $?>>8;
print LOGHANDLE "\$returnCode1=$returnCode1\n";
print LOGHANDLE "--------------BAD run\n";
open CMD2, '-|', #array_executable_and_parameters2 or die $#;
while (defined($line = <CMD2>)) {
print LOGHANDLE "-----\$line=$line-----\n";
}
close CMD2;
$returnCode2= $?>>8;
print LOGHANDLE "\$returnCode2=$returnCode2\n";
close(LOGHANDLE);
Take 2. After good advice in comments I have tried the IPC::Run. But something still does not work as expected. I seem to be missing how the looping from start (or pump?) to finish works, as well as how to get it to iterate when I do not know what the last output would be - as the examples everywhere mentions. So far I have now the following code, but it does not work line by line. It spits out listing of files in one go, then waits until the external loop is fully finished to print all the X's out. How do I tame it to the initial needs?
#! /usr/bin/perl
use IPC::Run qw( start pump finish );
#array_executable_and_parameters = ();
push(#array_executable_and_parameters,"/home/username/perl/myexecutable.sh"); #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
my $h = start \#array_executable_and_parameters, \undef, \$out, \$err ;
pump $h;# while ($out or $err);
print "1A. \$out: $out\n";
print "1A. \$err: $err\n";
$out = "";
$err = "";
finish $h or die "Command returned:\n\$?=$?\n\$#=$#\nKilled by=".( $? & 0x7F )."\nExit code=".( $? >> 8 )."\n" ;
print "1B. \$out: $out\n";
print "1B. \$err: $err\n";
Look at IPC modules, especially IPC::Cmd, IPC::Run and if not satisfied then IPC::Run3. There is a lot of details you would have to cover and those modules will make your life a lot easier.
OK, have got it to work, so far. Might have some issues - not sure about environment variables, like umask or language related or the system load when push is waiting/blocking, or how to replace die with capturing of all variables for status. Nevertheless for my purpose, seems to work well. Will see how it works on a real system.
#! /usr/bin/perl
BEGIN {
push #INC, '/home/myusername/perl5/lib/perl5'; #Where the modules from Cpan are
}
use IPC::Run qw( start pump finish );
#array_executable_and_parameters = ();
push(#array_executable_and_parameters,"/home/myusername/perl/myexecutable.sh"); #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
my $h = start \#array_executable_and_parameters, \undef, \$out, \$err ;
while (42) {
pump $h;# while ($out or $err);
if ($out eq '' and $err eq '') {last;}
print "1A. \$out: $out\n";
print "1A. \$err: $err\n";
$out = "";
$err = "";
}
finish $h or die "Command returned:\n\$?=$?\n\$#=$#\nKilled by=".( $? & 0x7F )."\nExit code=".( $? >> 8 )."\n" ;
print "1B. \$out: $out\n";
print "1B. \$err: $err\n";
The key was understanding how the blocking of pump works. All the manuals and help places kind of skipped over this part. So a neverending while which jumps out when pump lets go further without output was the key.
It is quite easy to read from the data section __DATA__ in Perl.
The following code uses the preexisting DATA handle:
foreach (<DATA>) {
print("- $_");
}
__DATA__
1
2
The End of The End
produces:
- 1
- 2
- The End of The End
However I haven't found any neat way of writing data to the section.
For example I've tried the following:
use feature say;
say STDERR 'Some Error';
say STDOUT 'Some Message';
say DATA 'Some Data';
__DATA__
foo bar
The print to STDOUT and STDERR works as expected:
perl write.pl 2> /dev/null
Some Message
But the print to DATA does nothing at all:
tail -2 write.pl
__DATA__
foo bar
I also have looked at several modules from CPAN, but I always only found support for read, not write.
Thanks for any hints and help!
You can write to the DATA section with a text editor.
Everything else is dark magic. Store input and program state in a separate file (or a database).
There is probably no good reason to this, but here is how:
#!/usr/bin/env perl
use strict;
use warnings;
seek DATA, 0, 2; # go to the end of the source file
my $offset = tell(DATA); # remember where that is
open my $f, '>>', $0 # open source file for appending
or die $!; # of course, you could use DATA instead of $f here
seek $f, $offset, 0; # go to the previously recorded position
print $f "This is a test\n"; # put something there
__DATA__
If the idea is to add things to what DATA can output, you can close it, then reopen it on a reference to a variable containing additional text, like this:
#!/usr/bin/perl
use strict;use warnings;
while(<DATA>) {
print("- $_");
}
close DATA;
my $additional_text = 'test2';
open DATA, '<', \$additional_text;
$additional_text .= "\ntest3";
while(<DATA>) {
print("- $_");
}
__DATA__
test
But you need to use the originally data contained in the DATA section before, I think.
Writing to DATA would change the script. This is a bad idea because DATA is open for read on the script itself. This program illustrates why this is a bad idea.
#!/usr/bin/perl
use strict;
use warnings;
seek DATA, 0, 0;
while( <DATA> ){
print;
}
__DATA__
1
2
3
I open a file and print some data on the screen , but I want to clean the screen after I output the data , I use clear; in the program but I don't see the effect of clean . It didn't clean .Does there
any command or function can let me do that?
I want to see the contain of a file only , but not to see some of the previous file on the screen ...
Here is my programs
`ls > File_List`;
open List , "<./File_List";
while(eof(List)!=1)
{
$Each = readline(*List);
chomp $Each;
print $Each;
print "\n";
`clear`;
open F , "<./$Each";
while(eof(F)!=1)
{
for($i=0;$i<20;$i++)
{
$L = readline(*F);
print $L;
}
last;
}
close(F);
sleep(3);
$Each = "";
}
close List;
Thanks
Your program uses non-idiomatic Perl. A more natural style would be
#!/usr/bin/env perl
use strict;
use warnings;
no warnings 'exec';
opendir my $dh, "." or die "$0: opendir: $!";
while (defined(my $name = readdir $dh)) {
if (-T $name) {
system("clear") == 0 or warn "$0: clear exited " . ($? >> 8);
print $name, "\n";
system("head", "-20", $name) == 0 or warn "$0: head exited " . ($? >> 8);
sleep 3;
}
}
Instead of writing a list of names to another file, read the names directly with opendir and readdir. The defined check is necessary in case you have a file named 0, which Perl considers to be a false value and would terminate the loop prematurely.
You don’t want to print everything. The directory entry may be a directory or an executable image or a tarball. The -T file test attempts to guess whether the file is a text file.
Invoke the external clear command using Perl’s system.
Finally, use the external head command to print the first 20 lines of each text file.
clear isn't working because the control sequence it outputs to clear the screen is being captured and returned to your program instead of being sent to the display.
Try
print `clear`
or
system('clear')
instead
The solution you provided doesn't work because the clear command is performed in a sub-shell. I suggest the use of a CPAN module (and multi platform supported): Term::Screen::Uni
Example:
use Term::Screen::Uni;
my $screen = Term::Screen::Uni->new;
$screen->clrscr;
Use system(), it works.
system("ls > File_List");
system("clear;");
I really don't know how to word this question, so I'll give an example of what I'm trying to do.
I have a script where I need to use the sleep function for a few seconds. But before I go to "sleep" I need to have a message say for example "Sleeping... ". But after it's done sleeping, I need to print "done". So what the terminal window will look like would be something like this: Sleeping... done, and this is my code for that:
#!/usr/bin/perl
print "Sleeping... ";
sleep(1);
print "done";
But what happens is, the script sleeps, and it then outputs Sleeping... done all at once.
Is it possible to print "Sleeping... ", then actually go to "sleep", and finally print "done", but have everything on one line?
You can enable auto flush on the file handle by setting $| to a non-zero value, such as:
local $| = 1;
So you're full script would be:
#!/usr/bin/perl
use strict;
use warnings;
local $| = 1;
print "Sleeping... ";
sleep(1);
print "done";
You can turn it back off by setting $| to 0, which you should do if this is just a small part of a larger script. Output is buffered by default for a reason.
Use
$| = 1
to disable buffered output. Then your script works as expected.
Alternatives:
select()->autoflush(); # Same as: $| = 1;
print "Sleeping... ";
sleep(1);
print "done\n";
print "Sleeping... ";
select()->flush(); # Same as: my $saved = $|; $| = 1; $| = $saved;
sleep(1);
print "done\n";
(You can replace select() with STDOUT if you don't otherwise use select().)
Is there any way to clear the STDIN buffer in Perl? A part of my program has lengthy output (enough time for someone to enter a few characters) and after that output I ask for input, but if characters were entered during the output, they are "tacked on" to whatever is entered at the input part. Here is an example of my problem:
for(my $n = 0; $n < 70000; $n++){
print $n . "\n";
}
chomp(my $input = <STDIN>);
print $input . "\n";
The output would include any characters entered during the output from that for loop. How could I either disable STDIN or flush the STDIN buffer (or any other way to not allow extra characters to be inserted into STDIN before calling it)?
It looks like you can accomplish this with the Term::ReadKey module:
#!perl
use strict;
use warnings;
use 5.010;
use Term::ReadKey;
say "I'm starting to sleep...";
ReadMode 2;
sleep(10);
ReadMode 3;
my $key;
while( defined( $key = ReadKey(-1) ) ) {}
ReadMode 0;
say "Enter something:";
chomp( my $input = <STDIN> );
say "You entered '$input'";
Here's what happens:
ReadMode 2 means "put the input mode into regular mode but turn off echo". This means that any keyboard banging that the user does while you're in your computationally-expensive code won't get echoed to the screen. It still gets entered into STDIN's buffer though, so...
ReadMode 3 turns STDIN into cbreak mode, meaning STDIN kind of gets flushed after every keypress. That's why...
while(defined($key = ReadKey(-1))) {} happens. This is flushing out the characters that the user entered during the computationally-expensive code. Then...
ReadMode 0 resets STDIN, and you can read from STDIN as if the user hadn't banged on the keyboard.
When I run this code and bang on the keyboard during the sleep(10), then enter some other text after the prompt, it only prints out the text I typed after the prompt appeared.
Strictly speaking the ReadMode 2 isn't needed, but I put it there so the screen doesn't get cluttered up with text when the user bangs on the keyboard.
I had the same problem and solved it by just discarding anything in STDIN after the processing like this:
for(my $n = 0; $n < 70000; $n++){
print $n . "\n";
}
my $foo=<STDIN>;
print "would you like to continue [y/n]: ";
chomp(my $input = <STDIN>);
print $input . "\n";
{ local $/; <STDIN> }
This temporarily - limited to scope of the block - sets $/, the input record seperator, to be undef, which tells perl to just read everything instead of reading a line at a time. Then reads everything available on STDIN and doesn't do anything with it, thus flushing the buffer.
After that, you can read STDIN as normal.