I need to read program output using Net::SSH2. My problem is some of that data hided under the bottom of program output. In ssh-mode I need to enter "Return" on my keyboard to look up next. This is awkward for using in a perl-script =).
I know that Net::OpenSSH does it well, but I really need to use Net::SSH2. Does anybody know, how can I get it?
Thnx!
UPD: Some code below
my $ch = $ssh2->channel();
$ch->blocking(0);
$ch->shell();
print $ch "dir\n";
print $_ while <$ch>;
In this code I print output of command with "--More--" prompt of the terminal at the bottom.
Simple Net::OpenSSH "capture" method returns a whole data at the same time:
my #dirlist = $ssh->capture('dir');
Is it possible to do same thing using Net::SSH2?
Related
I would need to automate some keyboard entries inside my perl script. For this, I use Win32::GuiTest module.
This works fine for all entries I need except for shift-end.
Here's what I send
Win32::GuiTest::SendKeys("+{END}");
but it seems that it only takes the {END}.
The weird thing is that
Win32::GuiTest::SendKeys("+(some text)");
works fine and sends SOME TEXT
In fact, I am unable to do +{} commands, it always take only the key inside the {}
On the other hand, commands with ^ (ctrl) or % (alt) work fine for example Win32::GuiTest::SendKeys("%{F4}") closes the window
does anybody would know why?
Thanks :)
Late answer, anyway, maybe it is of help to somebody...
Shift{foo} commands like Shift{End} need to be performed with low-level keybd_event via the SendRawKey wrapper. So this is what you are looking for:
SendRawKey(VK_LSHIFT, KEYEVENTF_EXTENDEDKEY);
SendKeys('{END}');
SendRawKey(VK_RSHIFT, KEYEVENTF_KEYUP);
SendRawKey(VK_LSHIFT, KEYEVENTF_KEYUP);
Full sample (copies a complete line into the clipboard):
use warnings;
use strict;
use Win32::Clipboard;
use Win32::GuiTest qw (:ALL); # Win32-GuiTest-1.63 used
print "place cursor now...\n"; sleep(5); print get_line();
sub get_line {
Win32::Clipboard()->Empty();
SendKeys('{HOME}');
SendRawKey(VK_LSHIFT, KEYEVENTF_EXTENDEDKEY);
SendKeys('{END}');
SendRawKey(VK_RSHIFT, KEYEVENTF_KEYUP);
SendRawKey(VK_LSHIFT, KEYEVENTF_KEYUP);
SendKeys('^c');
return Win32::Clipboard()->GetText();
}
I'm trying to get user input from a web page written in Perl and send it to a local program (blastp), then display the results.
This is what I have right now:
(input code)
print $q->p, "Your database: $bd",
$q->p, "Your protein is: $prot",
$q->p, "Executing...";
print $q->p, system("blastp","-db $bd","-query $prot","-out results.out");
Now, I've done a little research, but I can't quite grasp how you're supposed to do things like this in Perl. I've tried opening a file, writing to it, and sending it over to blastp as an input, but I was unsuccessful.
For reference, this line produces a successful output file:
kold#sazabi ~/BLAST/pataa $ blastp -db pataa -query ../teste.fs -out results.out
I may need to force the bd to load from an absolute path, but that shouldn't be difficult.
edit: Yeah, the DBs have an environmental variable, that's fixed. Ok, all I need is to get the input into a file, pass it to the command, and then print the output file to the CGI page.
edit2: for clarification:
I am receiving user input in $prot, I want to pass it over to blastp in -query, have the program blastp execute, and then print out to the user the results.out file (or just have a link to it, since blastp can output in HTML)
EDIT:
All right, fixed everything I needed to fix. The big problem was me not seeing what was going wrong: I had to install Tiny:Capture and print out stderr, which was when I realized the environmental variable wasn't getting set correctly, so BLAST wasn't finding my databases. Thanks for all the help!
Write $prot to the file. Assuming you need to do it as-is without processing the text to split it or something:
For a fixed file name (may be problematic):
use File::Slurp;
write_file("../teste.fs", $prot, "\n") or print_error_to_web();
# Implement the latter to print error in nice HTML format
For a temp file (better):
my ($fh, $filename) = tempfile( $template, DIR => "..", CLEANUP => 1);
# You can also create temp directory which is even better, via tempdir()
print $fh "$prot\n";
close $fh;
Step 2: Run your command as you indicated:
my $rc = system("$BLASTP_PATH/blastp", "-db", "pataa"
,"-query", "../teste.fs", "-out", "results.out");
# Process $rc for errors
# Use qx[] instead of system() if you want to capture
# standard output of the command
Step 3: Read the output file in:
use File::Slurp;
my $out_file_text = read_file("results.out");
Send back to web server
print $q->p, $out_file_text;
The above code has multiple issues (e.g. you need better file/directory paths, more error handling etc...) but should start you on the right track.
I have some Perl code I wrote for a client which, in part, resizes images using ImageMagick's convert.
This was done via backticks, capturing the output in this way:
$output = `convert foo.jpg foo.gif 2>&1`; # simplified example
Recently my client's hosting company made what he was told were "DNS changes", and since that date*, the command doesn't work, and not only that, there's no $output either.
Assuming something has happened like him being moved to a server which hasn't got ImageMagick, or a botched install or update of the library -- shouldn't I be getting something back from that? At least a "convert not found"? I've tried with system() as well and it's the same. Image not converted, nothing comes back.
Unfortunately, command-line access is not available.
* I suppose this could actually be a coincidence.
You can use convert's debug option to find out what it's doing.
To capture all outputs of a shell command use Capture::Tiny like this:
use Capture::Tiny 'capture';
my #command = qw( convert foo.jpg foo.gif );
my $return_code;
my ($stdout, $stderr) = capture {
$return_code = system( #command );
};
$stdout and $stderr will contain what it says on the tin. $return_code will return the return code of the task itself, which is traditionally 0 when successful or a non-zero numerical value when an error happened.
They may have no shell installed (think of perl/mod_perl being run in a chroot), such that backticks cannot be executed at all (since they involve calling sh -c 'convert ...'. Always check $? and $!.
I can setup a telnet connection in Perl no problems, and have just discovered Curses, and am wondering if I can use the two together to scrape the output from the telnet session.
I can view on a row, column basis the contents of STDOUT using the simple script below:
use Curses;
my $win = new Curses;
$win->addstr(10, 10, 'foo');
$win->refresh;
my $thischar=$win->inch(10,10);
print "Char $thischar\n";
And using the below I can open a telnet connection and send \ receive commands with no problem:
use net::telnet;
my $telnet = new Net::Telnet (Timeout => 9999,);
$telnet->open($ipaddress) or die "telnet open failed\n";
$telnet->login($user,$pass);
my $output = $telnet->cmd("command string");
... But what I would really like to do is get the telnet response (which will include terminal control characters) and then search on a row \ column basis using curses. Does anyone know of a way I can connect the two together? It seems to me that curses can only operate on STDOUT
Curses does the opposite. It is a C library for optimising screen updates from a program writing to a terminal, originally designed to be used over a slow serial connection. It has no ability to scrape a layout from a sequence of control characters.
A better bet would be a terminal emulator that has an API with the ability to do this type of screen scraping. Off the top of my head I'm not sure if any Open-source terminal emulators do this, but there are certainly commercial ones available that can.
If you are interacting purely with plain-text commands and responses, you can use Expect to script that, otherwise, you can use Term::VT102, which lets you screen scrape (read specific parts of the screen, send text, handle events on scrolling, cursor movement, screen content changes, and others) applications using VT102 escape sequences for screen control (e.g., an application using the curses library).
You probably want something like Expect
use strict;
use warnings;
use Expect;
my $exp = Expect->spawn("telnet google.com 80");
$exp->expect(15, #timeout
[
qr/^Escape character.*$/,
sub {
$exp->send("GET / HTTP/1.0\n\n");
exp_continue;
}
]
);
You're looking for Term::VT102, which emulates a VT102 terminal (converting the terminal control characters back into a virtual screen state). There's an example showing how to use it with Net::Telnet in VT102/examples/telnet-usage.pl (the examples directory is inside the VT102 directory for some reason).
It's been about 7 years since I used this (the system I was automating switched to a web-based interface), but it used to work.
Or you could use the script command for this.
From the Solaris man-page:
DESCRIPTION
The script utility makes a record of everything printed
on your screen. The record is written to filename. If no file name
is given, the record is saved in the file typescript...
The script command forks and creates a
sub-shell, according to the value of
$SHELL, and records the text from this
session. The script ends when the
forked shell exits or when
Control-d is typed.
I would vote also for the Expect answer. I had to do something similar from a gui'ish application. The trick (albeit tedious) to get around the control characters was to strip all the misc characters from the returned strings. It kind of depends on how messy the screen scrape ends up being.
Here is my function from that script as an example:
# Trim out the curses crap
sub trim {
my #out = #_;
for (#out) {
s/\x1b7//g;
s/\x1b8//g;
s/\x1b//g; # remove escapes
s/\W\w\W//g;
s/\[\d\d\;\d\dH//g; #
s/\[\?25h//g;
s/\[\?25l//g;
s/\[\dm//g;
s/qq//g;
s/Recall//g;
s/\357//g;
s/[^0-9:a-zA-Z-\s,\"]/ /g;
s/\s+/ /g; # Extra spaces
}
return wantarray ? #out : $out[0];
}
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.