如何在Perl中从Telnet中抓取输出?

8
我可以轻松地在Perl中建立telnet连接,并且刚刚发现了Curses,想知道我是否可以将两者结合起来从telnet会话中抓取输出。
我可以使用以下简单脚本按行列方式查看STDOUT的内容:
use Curses;
my $win = new Curses;
$win->addstr(10, 10, 'foo');
$win->refresh;
my $thischar=$win->inch(10,10);
print "Char $thischar\n";

使用以下方法,我可以轻松打开telnet连接并发送/接收命令:

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");

...但是我真正想做的是获取telnet响应(其中将包括终端控制字符),然后使用curses进行逐行/逐列搜索。有人知道我如何将这两个连接在一起吗?在我看来,curses只能在STDOUT上操作。


这个“scrape”的本质是什么?既然你最初认为可以使用curses,那么似乎你正在寻找一种实时响应屏幕数据的方法。你程序未来在telnet会话中的操作是否由过去的响应决定?还是你只是想在会话结束后检查/分析会话日志?感兴趣的数据是否真的与行/列有关?这是一个真正的应用程序还是学校练习? - gary
6个回答

5

Curses则恰恰相反。它是一个C库,用于优化从写入终端的程序进行屏幕更新,最初设计用于在缓慢的串行连接上使用。它没有能力从控制字符序列中抓取布局。

更好的选择是具有API并具有执行此类屏幕抓取功能的终端仿真器。我头脑中暂时不确定是否有任何开源终端仿真器可以这样做,但肯定有商业终端仿真器可用。


4
您可能需要类似于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;
                }
        ]
);

谢谢Vinko。Expect的问题在于它无法插入终端控制字符,因此它往往会被所有作为telnet数据返回的屏幕定位所困扰。 - Mike Atkinson
2
@Mike:expect已经被用于玩许多不同的Roguelike游戏 - 它不应该被ncurses搞混。 - Charles Stewart

4
如果您只是与纯文本命令和响应交互,可以使用Expect来编写脚本;否则,您可以使用Term::VT102,它允许您使用VT102控制字符(例如,基于curses库的应用程序)来屏幕抓取(读取屏幕特定部分、发送文本、处理滚动、光标移动、屏幕内容变化等事件)。

3
你正在寻找Term::VT102,它模拟了一个VT102终端(将终端控制字符转换为虚拟屏幕状态)。有一个示例展示了如何在Net::Telnet中使用它,在VT102/examples/telnet-usage.pl中可以找到(由于某种原因,示例目录位于VT102目录中)。
我已经有7年没有使用过它了(我自动化的系统已经转向基于Web的界面),但以前它是可以工作的。

太棒了!非常感谢你的帮助,真的非常感激 :) - Mike Atkinson

1

或者您可以使用script命令来实现此功能。

来自Solaris man-page:

描述

script实用程序记录在屏幕上打印的所有内容。记录写入文件名。如果没有给出文件名,则记录保存在文件typescript中...

script命令分叉并创建一个子shell,根据$SHELL的值记录此会话中的文本。当分叉的shell退出或键入Control-d时,脚本结束。


0
我也会投票支持Expect答案。我曾经从一个GUI应用程序中做过类似的事情。解决控制字符的技巧(虽然繁琐)是从返回的字符串中剥离所有杂项字符。这有点取决于屏幕抓取结束时的混乱程度。
以下是我从那个脚本中的函数作为示例:
# 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];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接