What is the simple way to keep the output stream exactly as it shown out on the screen (while interactive data used)? - perl

Let's say I have the following simple script:
print "ID : ";
$ID = <>;
system (`comp program $ID`);
exec "task --shell";
When I use:
perl foo.pl | tee log.txt
The showing up problem is getting on screen a blink sign echo (waiting for the ID enter) before I even see the "ID : " (print instruction).
I need to keep on a log file all running output script (very long), notice that at the start of the run I have an interactive part that also need to be kept.
Is there a way inside PERL script or outside it, that keep the output stream exactly as it shown out on the screen, and what do you think is the simple & efficient way to conduct it?
I've noticed the IO::Tee , File::Tee moudles and Log4perl - someone can help me find the best way to use it ?

The best approach I have seen is to use script program.
You run script (program) - it gives you shell. in this shell you run anything you want. When you're done, you exit this shell, and everything that was on your screen will be in typescript file. Including all control codes.
There is also "bonus" - you can replay saved session with scriptreplay.

It's possible that your script is asking for user input before printing the "ID: " prompt because you don't have autoflush turned on. Try adding the following:
$|++;
print "ID : ";
$ID = <>;
system (`comp program $ID`);
exec "task --shell";
If this works then you might look at the Perl documentation about output buffering. perldoc -q buffer will explain what's happening in more detail as well as show some alternate ways of turning on autoflush.
If this doesn't solve the problem then it's possible that the tee command is buffering your output. In this case I would use script or screen as suggested by depesz or Manni

You could run everything inside a screen session with logging enabled.

Related

Get both STDOUT and STDERR of a program or script to a variable in perl [duplicate]

This question already has answers here:
How can I get the CPU time for a perl system call?
(3 answers)
Closed 5 years ago.
I did lot of google to redirect the both STDOUT and STDERR of a program to a variable in perl, Initially i tried using both open and backticks, but failed to capture Error signal to the variable.
I am posting this question because Now I got the solution and hope it may helps others..
To get both STDOUT and STDERR into a variable use following code snippet using backticks (``).
Make sure 2>&1 is places at the end of your command, to redirect STDERR in to STDOUT.
When wrong command provided,
my $Temp_return;
$Temp_return = `lse 2>&1`;
print "return = " . $Temp_return . "\n";
Error OutPut is,
return = 'lse' is not recognized as an internal or external command, operable program or batch file.
For correct command you will get the result accordingly.
As an additional information different methods for executing the command in Perl are.
system() : If you want to execute a command and not interested in reading console output of the executed command.
exec : When you don't want to return to the calling Perl script. use same.
backticks : When you want to store /read the console output of the command into a Perl variable. Initial I mistakenly thought, required to use Single cores('') instead, for backticks (``) and get confused, because its almost similar to Single cores(''), please give attention.
open() : When you want to pipe the command (as input or output) to your script.
Hope it could be helpful for you..... :)
BR,
Jerry James

Output stdin, stdout to file and console using Perl

I am trying a simple questionnaire using perl. I want to record the responses in a log file as and when a user inputs it. I'm having problem in redirecting the stdin to file.
Below is the code I implemented. Refer this.
open my $tee, "|-", "tee some_file.out";
print $tee "DO you want to continue?(y/n)\n";
$var=<STDIN>;
$var =~ s/[\n\r\f\t]//g;
if($var eq "y"){
print $tee "Enter\n";
}
close $tee;
The output I'm getting now is, only after user input is provided the question is printed.
#in console
y
DO you want to continue?(y/n)
Enter
#some_file.out
DO you want to continue?(y/n)
Enter
Below is the expected output:
#in console
DO you want to continue?(y/n)
y
Enter
#some_file.out
DO you want to continue?(y/n)
y
Enter
I also found Duplicate stdin to stdout but really couldn't achieve what I want to.
Am I missing something?!
Is there any cleaner solution available?
First of all, never use the phrase "redirecting the stdin to..." because stdin is input. It doesn't go to anything. It comes from somewhere.
It seems that what you expected is to have a copy of $var appear in your log file. Since you never printed $var to $tee there's no way that could happen.
So why did you think $var would appear in the log file? From the way you have shown us a copy of the log file next to a copy of what you see on the terminal, I guess that your reasoning went something like this:
The tee put all of the output into the log file
The tee also put all of the output on the terminal
My program didn't output anything else besides what went into the tee
The screen contents should match the log file
But there's a hidden assumption that's required to reach the conclusion:
3a. Nothing else was written to the terminal besides my program's output
And that's the part which is incorrect. When you type y into the terminal while your program is running, the terminal itself echoes what you type. It prints a copy in the terminal window, and also sends the character to your program's stdin. The y that you see on screen is not part of your program's output at all.
Since the echoing is done by the terminal and not by your program, you can't instruct it to also send a copy to your log file. You need to explicitly print it there if you want it to be logged.
You can ask the terminal to stop echoing, and then you take responsibility for printing the characters as they are typed so the user can see what they're typing. If you want to try that, see the Term::ReadKey module.
Or if what you really want is a complete record of everything that appeared on the terminal during the run of your program, maybe you should run it in the standard unix tool script, which is made for exactly that purpose.
(Side note: Did you know about the IO::Tee module? you can have teed output without an external process)

Perl execute command, capture and display output

I need to execute a command from my Perl script, which is going to take a while (1-2 hours). I want to be able to see the output of the command, so that my script can check everything went OK, but as it takes such a long time, I'd like the user to see the commands output while it runs, too.
What I've tried:
backticks - Can only get output when command is finished
system - Can only get output when command is finished
open - Almost perfect - but the commands output is buffered, meaning users don't see an update for a long time. Internet is full of suggestions to set $| = 1 but apparently this only affects input buffering and doesn't work
Piping to tee - Similar results to open - 'tee' only seems to print later
Re-directing output and Using Proc::Background and File::Tail - Almost perfect again, but can't think of an elegant way to stop the print loop
Would love to have a suggestion!
Edit: I've accepted Barmars answer. I believe it works because Expect.pm uses a pseudo-terminal. Just to others looking at this question in future, this is how I've implemented it:
my $process = Expect->spawn($command, #params) or die "Cannot spawn $command: $!\n";
while ($process->expect(undef))
{
print $process->before();
}
Using Expect.pm should disable output buffering in the command.

How to debug a perl script used inside another perlscript?

How to debug a perl script used inside another perlscript. We can debug both separately. Is it possible to debug in one step?
Ex:
!/user/bin/perl
my $param= 8;
my #res=\`perl extract.pl $config`;
print "The results is .... #res\n";
Similarly, can we debug the perl scripts used inside shell script?
Thanks.
You can add -d switch to the shebang line of the script you are invoking:
#!/usr/bin/perl -d
A dynamic solution:
my $dswitch = $INC{"perl5db.pl"} ? "-d" : "";
my #res=`perl $dswitch extract.pl $config`;
$INC{"perl5db.pl"} is true when you are using the debugger, and false when you are not.
The "correct" thing to do would be to change your design so you are not calling the other script through a system command. Then you would have a single debugging environment.
The best design would be to make extract.pl into a module.
You could also use the do command to import the code from the other file: do 'extract.pl'. Then the only change needed would probably be the way you pass $config in.
Either of these approaches would be quite simple to implement and would have other benefits for continued development. Maybe you have a good reason not to do it this way, but I would consider this first if you haven't already.
Just debug extract.pl passing in the value in $config. If you don't know what it is, the age-old printf will do that for you.
say "\$config='$config'";
And then do:
perl -d extract.pl 'Stuff that printed in config'.
As the two are isolated processes, the only communication is what is passed on the command line and what is printed by the called script. So the called script should be capable of being tested without the calling script. (Unless you've set up some sort of IPC or shared memory or something tricky like that.)

In Perl, how do I determine if there's a standard input present?

I've got a script that grabs standard input:
&process_input
sub process_input {
while(<STDIN>) {
$log_data .= $_;
}
}
When I run the script:
myscript.pl -param1=a -param2=b
I get stuck in this subroutine. Everything runs OK if I do:
echo "" | myscript.pl -param1=a -param2=b
How do I determine if I even have a standard input?
I would have thought that while(<STDIN>) would return false and not run, but I'm guessing it's actually waiting for you to type something in that why it's 'stuck'.
You want to check where your STDIN (STanDard INput) is coming from: another application or a terminal. In your case, it's the second option, causing a read operation to stall the process until the user inputs something. For a solution, see How can I tell if STDIN is connected to a terminal in Perl?.
if (-t STDIN) {
# input attached to terminal and will probably ask user
} else {
# input from other process
}
There's also IO::Interactive that might do better/more reliable checking.
The statement <STDIN> does not return until you press Enter on the console. If you want to get around this, I believe that you can use IO::Handle to wrap STDIN, and call $stdin->blocking(0) to enable non-blocking I/O.
That's normal. Standard usage for Unix tools is to use STDIN if no input file is given as an argument. Try cat, less, grep, etc. It's up to the caller to provide input, if only
tool < /dev/null
I strongly advise against trying to determine if "input" is available as it will introduce problems no matter how you achieve this. Specifically, avoid -t since it's problematic to fake a terminal when needed. Instead, rely on a more conventional interface.
If you want to make it possible to pass no input to your tool, it's weird that you'd be using STDIN in the first place. One would normally use an optional argument.
tool --foo file
tool --foo <( echo "" )
Another option would be to request that the user tells you when there is no input.
tool --batch
In order to help you with the design problems of your interface, it would really help to know what your tool does.
Your program will continue when the user types Ctrl + D, the end-of-file character.