这是我的第一个perl程序:
我想确保如果我离开我的机器一段时间,那么这个脚本ssh到我们的主服务器并杀死我在那里的所有进程。 (当我去吃午餐时,我一直忘记杀死他们,他们占用了大量的CPU和记忆)。
我已经走到这一步了,屏幕保护程序激活杀戮开始后15分钟。
#!/usr/bin/perl
my $cmd = "dbus-monitor --session \"type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'\"";
open (IN, "$cmd |");
while (<IN>) {
if (m/^\s+boolean true/) {
print "*** Screensaver is active ***\n";
print "*** Sleeping before megadeath....\n";
sleep(15*60);
print "*** killing all jla processes on anvil...\n";
$result = `ssh anvil pkill -u jla`;
print "*** should all be dead\n";
print $result;
} elsif (m/^\s+boolean false/) {
print "*** Screensaver is no longer active ***\n";
}
}
但我想要的是在监控键盘的同时等待15分钟。如果说,'N'键被按下(在脚本运行的终端中),那么我想中止查杀并返回监视屏幕保护程序。如果屏幕保护程序在我喝咖啡时亮起,这将给我一条逃生途径。
某种邦德风格的倒计时也很不错。
实际上更好的方法是注意屏幕保护程序何时解锁,如果是,则停止倒计时,返回监控模式。然后我甚至不用担心记得按N.
答案 0 :(得分:4)
如果你的perl
有线程支持,你可以这样做:
#!/usr/bin/perl
use warnings; use strict;
use threads;
use threads::shared;
use Term::ReadKey;
my $DONT_TERMINATE :shared;
my $TIMEOUT = 5;
threads->new( sub { wait_for_keypress('n', $TIMEOUT) })->detach;
threads->new( sub { countdown($TIMEOUT) })->join;
sub countdown {
my ($timeout) = @_;
while ( 1 ) {
my $elapsed = time - $^T;
last if $elapsed >= $timeout;
return if $DONT_TERMINATE;
print $timeout - $elapsed, "\n";
sleep 1;
}
print "Killing some processes\n";
}
sub wait_for_keypress {
my ($key, $timeout) = @_;
my $input = ReadKey( $timeout );
$DONT_TERMINATE = (defined($input) and ($input eq $key));
return;
}
如果您没有线程支持,可以使用Coro。
注意:我删除了我的Coro示例,因为它无法正常工作。如果我搞清楚的话,我会再发一次。
答案 1 :(得分:3)
我使用select
(通过IO::Select
),它可以让您检查文件句柄是否有现成数据。但是,您不能使用<>
和select
之类的“缓冲”IO运算符,因此这比您想要的更复杂(您必须使用sysread
并维护自己的缓冲区) 。以下是观看屏幕保护程序活动的方法,如果已经打开了15分钟,可以执行某些操作。
use IO::Select;
my $s = IO::Select->new();
# ... Start dbus-monitor as above ...
$s->add(\*IN);
my $buf = '';
my $started = 0;
my $waitfor = 15*60;
while ( 1 ) {
# Read from all ready filehandles (timeout if none ready after 1 sec)
foreach my $fh ( @ready = $s->can_read(1) ) {
sysread($fh, $buf, 128, length($buf));
}
# Handle each complete line of input
while ( $buf =~ s/^(.+)\n// ) {
my $line = $1
# ... Do something ...
if ( $line =~ m/^\s+boolean (true|false)/ ) {
if ( $1 eq 'true' ) { $started = time; print "Starting timer\n" }
else { $started = 0; print "Canceled timer\n" }
}
}
next unless $started;
# Screensaver is on, how long has it been running?
my $timeleft = $started+$waitfor-time;
if ( $timeleft <= 0 ) {
print "The screensaver has been on for at least 15 minutes\n";
# ... Do something ...
$started = 0; # Don't do this again until the screensaver is restarted
}
else {
# You can print out an updated countdown
print "$timeleft seconds left\n";
}
}
我根本没有对此进行过测试,但它可能足以让它发挥作用。
<子> P.S。它不适用于Windows,select
仅适用于套接字。
答案 2 :(得分:3)
Sinan和nandhp的解决方案将适用于此任务。 threads
和select
是Perl程序员工具库中的强大工具,但我不愿意为某人的“首次perl(sic)程序”提出建议。所以我建议采用另一种方法。
为了过度简化此问题的陈述,我们希望在发生其他事情时(屏幕保护程序已激活15分钟)执行某些操作(触发命令以终止远程服务器上的进程)。
use strict;
use warnings;
initialize_program();
until (something_happens()) {
sleep 60;
}
do_something();
exit;
do_something
部分很简单:
sub do_something {
print "*** killing all jla processes on anvil...\n";
$result = `ssh anvil pkill -u jla`;
print "*** should all be dead\n";
print $result;
}
对于程序的something_happens
部分,我建议在后台进程中将dbus-monitor
输出发送到文件,并在想知道屏幕状态时从文件中读取保护程序。 dbus-monitor
程序生成输出非常慢,从Perl文件句柄读取将会阻止(除非您了解并使用select
)。
我将稍微调整dbus-monitor
命令。每次屏幕保护程序状态发生变化时,此命令都会打印出时间戳:
my $command = q[dbus-monitor --session "type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'" | perl -ne 'print time," $_" if /boolean/'];
我们将通过执行以下命令启动我们的程序:
sub initialize_program {
# broken into multiple lines for readability
my $command = q[dbus-monitor --session ]
. q["type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'"]
. q[ | perl -ne 'print time," $_" if /boolean/'];
system("$command > /tmp/screensavermonitor &");
}
现在要查看屏幕保护程序是否有效以及有多长时间,我们会偶尔解析/tmp/screensavermonitor
。
sub something_happens {
open (my $fh, '<', '/tmp/screensavermonitor') or return do { warn $!;0 };
my @output = <$fh>;
close $fh;
# we only care about the last output
my $state = pop @output;
if (!defined $state) {
# maybe there's no output yet
return 0;
}
if ($state =~ /false/) {
# screensaver is not active
return 0; # event hasn't happened yet
}
if ($state =~ /true/) {
# screensaver is active -- but for how long?
# start time (in seconds since the epoch) is included in output
my ($screensaver_start_time) = ($state =~ /(\d+)/);
if (time - $screensaver_start_time >= 15 * 60) {
return 1;
} else {
return 0;
}
}
return 0;
}
答案 3 :(得分:1)
正如暴徒所说,线程和select
使这一点过于复杂。所以这里有Term::ReadKey的简单和简单的东西,它可以让你首先做你要求的事情:等待按键,但如果在15分钟内没有按下按键,则超时。< / p>
#!/usr/bin/env perl
use strict;
use warnings;
use Term::ReadKey;
my $cmd = "dbus-monitor --session \"type='signal', interface='org.gnome.ScreenSaver',member='ActiveChanged'\"";
open(IN, "$cmd |");
ReadMode 'cbreak'; # return keypress immediately without waiting for "Enter"
while (<IN>) {
if (m/^\s+boolean true/) {
print "*** Screensaver is active ***\n";
print "*** Sleeping before megadeath....\n";
my $key = ReadKey 900; # timeout after 900 seconds = 15 minutes
if (defined $key) {
print "*** A key was pressed; megadeath averted\n";
} else {
print "*** killing all jla processes on anvil...\n";
my $result = `ssh anvil pkill -u jla`;
print "*** should all be dead\n";
print $result;
}
} elsif (m/^\s+boolean false/) {
print "*** Screensaver is no longer active ***\n";
}
}
ReadMode 'restore'; # back to normal input mode
(代码在语法上是正确的,但尚未运行,因此未经过全面测试。您可能还需要设置&#39; noecho&#39; ReadMode以及&#39; cbreak&#39;阻止禁止兆字节出现在屏幕上的按键。)