Linux 每隔2小时自动退出perl脚本

Linux 每隔2小时自动退出perl脚本,linux,perl,command-line,scheduled-tasks,redhat,Linux,Perl,Command Line,Scheduled Tasks,Redhat,我有一个perl脚本,我需要它在Linux机器上每两个小时结束一次。我本来打算编写一个单独的脚本,并将其添加到cron.d上以实现这一点,但我想要一个更简单的方法。它必须优雅地退出,因为在执行CTRL+C之后,它会写入一个日志文件,杀死它不会写入该文件。您可以通过在$SIG{INT}中设置一个子例程来捕获CTRL-C发送的信号: $ perl -e '$SIG{INT} = sub { print "Caught signal, cleaning up\n"; exit 0 }; while(1

我有一个perl脚本,我需要它在Linux机器上每两个小时结束一次。我本来打算编写一个单独的脚本,并将其添加到cron.d上以实现这一点,但我想要一个更简单的方法。它必须优雅地退出,因为在执行CTRL+C之后,它会写入一个日志文件,杀死它不会写入该文件。

您可以通过在
$SIG{INT}
中设置一个子例程来捕获CTRL-C发送的信号:

$ perl -e '$SIG{INT} = sub { print "Caught signal, cleaning up\n"; exit 0 }; while(1) {}'

在sub中进行清理,就可以了。

您可以在脚本开头设置警报,并为其提供处理程序:

alarm 60 * 60 * 2;
local $SIG{ALRM} = sub {
    warn "Time over!\n";
    # Do the logging here...
    exit
};

问题是如何重新启动脚本。

包装器使事情变得简单

#!/usr/bin/perl

# usage:
#    restarter program [arg [...]]

use strict;
use warnings;

use IPC::Open3 qw( open3 );
use POSIX      qw( WNOHANG );


use constant RESTART_AFTER  => 2*60*60;
use constant KILL_INT_WAIT  => 30;
use constant KILL_TERM_WAIT => 30;
use constant WAIT_POLL      => 15;


sub start_it {
   open(local *NULL, '<', '/dev/null')
      or die($!);

   return open3('<&NULL', '>&STDOUT', '>&STDERR', @_);
}


sub wait_for_it {
   my ($pid, $max_wait) = @_;

   my $end_time = time + $max_wait;

   while (1) {
      if (waitpid($pid, WNOHANG) > 0) {
         return 1;
      }

      my $time = time;
      if ($end_time >= $time) {
         return 0;
      }

      sleep(1);
   }
}


sub end_it {
   my ($pid) = @_;
   kill(INT => $pid)
      or die($!);

   return if wait_for_it($pid, KILL_INT_WAIT);

   kill(TERM => $pid)
      or die($!);

   return if wait_for_it($pid, KILL_TERM_WAIT);

   kill(KILL => $pid)
      or die($!);

   waitpid($pid, 0);
}


sub run_it {
   my $end_time = time + RESTART_AFTER;

   my $pid = start_it(@_);

   while (1) {
      if (waitpid($pid, WNOHANG) > 0) {
         last;
      }

      my $time = time;
      if ($end_time >= $time) {
         end_it($pid);
         last;
      }

      my $sleep_time = $end_time - $time;
      $sleep_time = WAIT_POLL if $sleep_time > WAIT_POLL;  # Workaround for race condition.
      sleep($sleep_time);
   }

   my $status = $?;

   if    ($? & 0x7F) { warn("Child killed by signal ".($? & 0x7F)."\n"); }
   elsif ($? >> 8)   { warn("Child exited with error ".($? >> 8)."\n"); }
   else              { warn("Child exited with succcesfully.\n"); }
}


run_it(@ARGV) while 1;
#/usr/bin/perl
#用法:
#重启程序[arg[…]]
严格使用;
使用警告;
使用IPC::Open3QW(Open3);
使用POSIX qw(WNOHANG);
在=>2*60*60之后使用常量重启_;
使用常数KILL_INT_WAIT=>30;
使用常数KILL_TERM_WAIT=>30;
使用常量WAIT_POLL=>15;
启动它{
打开(本地*NULL',&STDERR',@908;);
}
等着吧{
我的($pid,$max_wait)=@;
我的$end\u time=时间+max\u等待;
而(1){
如果(waitpid($pid,WNOHANG)>0){
返回1;
}
我的$time=时间;
如果($end\u time>=$time){
返回0;
}
睡眠(1);
}
}
分机{
我的($pid)=@;
kill(INT=>$pid)
或者死($!);
如果等待,则返回($pid,KILL\u INT\u wait);
终止(期限=>$pid)
或者死($!);
如果等待,则返回($pid,KILL\u TERM\u wait);
kill(kill=>$pid)
或者死($!);
waitpid($pid,0);
}
分管{
my$end\u time=时间+重新启动\u之后;
我的$pid=启动它(@);
而(1){
如果(waitpid($pid,WNOHANG)>0){
最后;
}
我的$time=时间;
如果($end\u time>=$time){
结束它($pid);
最后;
}
我的$sleep\u time=$end\u time-$time;
$sleep\u time=WAIT\u POLL如果$sleep\u time>WAIT\u POLL;#比赛条件的解决方法。
睡眠(睡眠时间);
}
我的$status=$?;
if($?&0x7F){warn(“被信号杀死的子对象”。($?&0x7F)。“\n”);}
elsif($?>>8){warn(“子项因错误退出”。($?>>8)。“\n”);}
else{warn(“子项已成功退出。\n”);}
}
在1时运行它(@ARGV);

您可能希望将发送给处理程序的信号转发给子进程。

您不需要编写脚本来终止进程:
pkill-f[pattern]
(这会发送
SIGTERM
,但您可以指定任何您喜欢的信号)。但我很好奇,为什么你的脚本一次运行几个小时?该脚本生成随机VOIP呼叫以进行呼叫质量测试以重新启动脚本,我希望cron.d能够做到这一点that@EnigmaRenegade:您仍然应该验证脚本是否过早结束或根本没有停止。是否有方法可以制作一个单独的脚本来完成此操作?听起来脚本已经捕获了SIGINT(因此它在Ctrl-C上写入日志)。问题是如何从外部杀死剧本。@AndrewMedico--不,我不认为这是真的。这个问题说(加上强调)“它必须优雅地退出,因为在执行CTRL+C之后,它会写入一个日志文件,而终止它不会写入该文件。”这对我来说意味着没有信号处理程序。如果有的话,只需在捕获信号时关闭文件句柄,或者(如果您想对此有点草率)刷新缓冲区。