Redirect stdout to a file in tcl - redirect

I know this has been asked before.
But hear me out once..
I'm working on a Cisco router (IOS).
I have to write a script which executes some commands and redirect their output to a file. But instead of using tee for every command, I want to open a file then run the commands , whose output will be redirected to the file and then close the file.
And even the redirection operator > is not working or the following answer:
How can I redirect stdout into a file in tcl

The fact that the solutions in that question aren't working for you is informative: the real issue that you are experiencing is that Tcl commands do not normally write to stdout anyway (except for puts, which has that as its main job, and parray, which is a procedure that uses puts internally). The “write to stdout” that you are used to is a feature of the interactive Tcl shell only.
To capture the result of all commands while running a script, you need a wrapper like this:
trace add execution source leavestep log_result
proc log_result {cmd code result op} {
puts stdout $result
}
source theRealScript.tcl
You'll find that it produces a lot of output. Cutting the output down is a rather useful thing, so this reduces it to just the immediately-executed commands (rather than everything they call):
trace add execution source enterstep log_enter
trace add execution source leavestep log_result
proc log_enter {args} {
global log_depth
incr log_depth
}
proc log_result {cmd code result op} {
global log_depth
if {[incr log_depth -1] < 1 && $result ne ""} {
puts stdout $result
}
}
source theRealScript.tcl
You'll probably still get far more output than you want…

Related

Running a shell command in a perl loop, using a variable from the perl loop

I need to run a shell command within a perl script(It needs to be a shell command) but I need to use a variable in a perl loop
Ex. doing something like
#!/usr/bin/perl
for my $i (0..9) {
`echo $i`;
}
should produce the output
0
1
2
3
4
5
6
7
8
9
You can do it in several ways, depending on what exactly is needed. Generally, this involves use of system, backticks (with operator form qx), or open. There is also a number of great modules for it.
The qx returns its STDOUT to the program. If you need it assign it to a variable.
for my $i (0..9) {
my $cmd = "external-command $i";
my $cmdout = qx($cmd); # needs error checking ($?)
print $cmdout; # process as needed
}
The output can also be assigned to an array, when each line becomes one array element. Depending on what your command is you may have to pay close attention to how to quote it correctly, and String::ShellQuote can help a lot. If you want to get the command's STDERR as well use
my $cmd = "external-command $i 2>&1";
See qx in perlop (or readpipe), which also discusses error checking; also see $? in pervar.
If you don't need the command's output in your script, or specifically want it to wind up together with your script's other output, use system. Commands run by it inherit the script's STDOUT and STDERR so what they print to standard streams will go where your script's output goes (unless it is redirected by the command invocation).
for my $i (0..9) {
my $cmd = "external-command $i";
my $ret = system($cmd);
if ($ret != 0) {
warn "Error running $cmd: $ret";
# interrogate further, see documentation for 'system'
}
}
The error handling above is rudimentary, see docs for more. Finally, there are various forms of open that run a process. For example, see it in perlfaq8, along with a full review of what you are asking.
And then there are modules, that make this kind of work far easier and better. From simple to more capable, some of well-known ones are IPC::System::Simple, Capture::Tiny, IPC::Run3, and IPC::Run. Also see links (examples) assembled in this post, for example.
A command can be executed via a shell or not (but via execvp) depending on how exactly it is invoked, as discussed in the docs. The phrase "shell command" strictly refers to a facility provided by the shell itself, in which case you of course want to run it that way. However, it is also often used for a program that we normally execute by typing in a terminal, so it gets executed by the shell. In this case you may (or may not) want to bypass the shell.
Also note that the above all block (do not return control before completing), so the script continues only after they're done. This seems to be what you want.
If you just want to pass through the shell output, simply do
print STDOUT `echo $i`;

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: Testing an input reader?

Is there a way to automatically test using the standard Test etc. modules whether a Perl program is reading input from e.g. STDIN properly? E.g. testing a program that reads two integers from STDIN and prints their sum.
It's not 100% clear what you mean, I'll asnswer assuming you want to write a test script that tests your main program, which as part of the test needs to have test input data passed via STDIN.
You can easily do that if your program outputs what it reads. You don't need a special test module - simply:
Call your program your're testing via a system call
redirect both STDIN and STDOUT of tested program to your test script, using
IPC::Open2 module to open both sides via pipes to filehandles,
... OR, build your command to redirect to/from files and read/write the files in the test script
Check STDOUT from tested program that you collected in the last step to make sure correct values are printed.
If you want to test if STDIN is connected to a terminal, use -t, as in:
if( -t STDIN ){
print "Input from a terminal\n";
}else{
print "Input from a file or pipe\n";
}
See http://perldoc.perl.org/perlfunc.html#Alphabetical-Listing-of-Perl-Functions

Write to console when stdin/stderr/stdout redirected in Activestate Perl

I have the following code to write to the Windows command console:
use Win32::Console;
my $console = new Win32::Console(Win32::Console::STD_ERROR_HANDLE());
my $defaultAttribute = $console->Attr();
my $defaultFG = ($defaultAttribute & 0x0F);
my $defaultBG = ($defaultAttribute & 0xF0);
$console->Attr($defaultBG | $Win32::Console::FG_LIGHTGREEN);
$console->Write("blah blah");
$console->Attr($defaultAttribute);
This code fails if the user redirects STDERR when invoking my script:
perl myscript.pl 2> foo
How can I obtain a handle to the Win32 console the process is attached to without reference to one of the standard handles so that it doesn't matter what redirections the user makes?
The effect I want is to be able to write a message on the console immediately following normal program output regardless of any redirection in a similar way to the bash builtin time command. Essentially, similar to opening and writing to /dev/tty in Unix.
I've tried my $console = new Win32::Console() to allocate a new console followed by $console->Display() but this does completely the wrong thing.
After asking this question, I delved a bit deeper and was able to solve it by using a nasty hack:
use Win32API::File qw(createFile);
use Win32::Console;
my $handle = createFile('CONOUT$', 'rwke') or die "conout\$: $^E\n";
# my $console = new Win32::Console($handle) or die "new console: $^E\n";
my $console = bless {handle => $handle}, 'Win32::Console';
I looked at the code for the new() function inside Win32::Console and saw that it just creates a hash containing the handle to a console. If the parameter specifies stdin/stdout/stderr, it just retrieves the associated handle otherwise it creates a new console screen buffer and uses the handle for that.
So I just manually created the Win32::Console object containing a handle to the console returned by CreateFile.
So now perl myscript.pl > nul 2> nul < nul will write blah blah on the screen immediately below the command line.
I'll accept a better answer if someone comes up with one.
According to the AllocConsole() docs (C++ docs, but the concepts are the same):
"A process can be associated with only one console, so the AllocConsole function fails if the calling process already has a console. A process can use the FreeConsole function to detach itself from its current console, then it can call AllocConsole to create a new console or AttachConsole to attach to another console."
Since your console is already redirected it doesn't look like there's anything you can do about it; even if you detach the console and allocate a new one, the new console inherits the redirection. In C++ you would use the SetStdHandle() API to force the standard handles to point to a different file or device, but I can't find any Perl equivalent of that.

Reopen STDERR/STDOUT to write to combined logfile with timestamps

I basically want to reopen STDERR/STDOUT so they write to one logfile with both the stream and the timestamp included on every line. So print STDERR "Hello World" prints STDERR: 20130215123456: Hello World. I don't want to rewrite all my print statements into function calls, also some of the output will be coming from external processes via system() calls anyway which I won't be able to rewrite.
I also need for the output to be placed in the file "live", i.e. not only written when the process completes.
(p.s. I'm not asking particularly for details of how to generate timestamps, just how to redirect to a file and prepend a string)
I've worked out the following code, but it's messy:
my $mode = ">>";
my $file = "outerr.txt";
open(STDOUT, "|-", qq(perl -e 'open(FILE, "$mode", "$file"); while (<>) { print FILE "STDOUT: \$\_"; }'));
open(STDERR, "|-", qq(perl -e 'open(FILE, "$mode", "$file"); while (<>) { print FILE "STDERR: \$\_"; }'));
(The above doesn't add dates, but that should be trivial to add)
I'm looking for a cleaner solution, one that doesn't require quoting perl code and passing it on the command line, or at least module that hides some of the complexity. Looking at the code for Capture::Tiny it doesn't look like it can handle writing a part of output, though I'm not sure about that. annotate-output only works on an external command sadly, I need this to work on both external commands and ordinary perl printing.
The child launched via system doesn't write to STDOUT because it does not have access to variables in your program. Therefore, means having code run on a Perl file handle write (e.g. tie) won't work.
Write another script that runs your script with STDOUT and STDERR replaced with pipes. Read from those pipes and print out the modified output. I suggest using IPC::Run to do this, because it'll save you from using select. You can get away without it if you combine STDOUT and STDERR in one stream.