Linux 获取当前完整的crontab行,并为每行创建唯一的结果电子邮件标题(发件人、主题等)
我有一个巨大的crontab,在linux机器上有许多脚本。我需要能够a)更改cronjob结果电子邮件的主题和/或发件人,因为默认值太长。b) 通过一个集中的解决方案做到这一点。c) 只需要对crontab本身进行最小的更改 例如,此crontab行:Linux 获取当前完整的crontab行,并为每行创建唯一的结果电子邮件标题(发件人、主题等),linux,perl,email,cron,pipe,Linux,Perl,Email,Cron,Pipe,我有一个巨大的crontab,在linux机器上有许多脚本。我需要能够a)更改cronjob结果电子邮件的主题和/或发件人,因为默认值太长。b) 通过一个集中的解决方案做到这一点。c) 只需要对crontab本身进行最小的更改 例如,此crontab行: 0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 创建此电子邮件主题: Cron <cronuser@myserver> /path/to/scr
0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3
创建此电子邮件主题:
Cron <cronuser@myserver> /path/to/script1 | /path/to/script2 | /path/to/script3
不幸的是,cron在主题行的“描述性词语”之前仍然有太多不必要的内容,所以这仍然是不可用的
我要创建的内容:
一些通用的东西,比如:
0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl
pl将通过获取该cron行上的命令来构建主题行,去掉路径和参数,并通过电子邮件向我发送此消息(仅当任何脚本出现故障时可选):
最终版本,根据Jhnc的建议
(为了清晰起见,对目标进行了编辑,以及为什么迄今为止尝试的选项还不够。)确定管道
如果总是使用唯一的参数调用coolmailer.pl
,则只需从cronjobs列表中将其grep:
#!/usr/bin/perl -wT
$ENV{PATH} = '/sensible:/path';
my ($pipeline) = grep /\|\s+$0\s+$ARGV[0]/, `crontab -l`;
$pipeline ||= "oops";
# ... mung $pipeline ...
# ... do mail stuff ...
检查管道故障 如果从以下位置重写cronjob条目:
/path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl
致:
然后,您可以手动构造管道并控制管道成员状态信息。(这也会直接为您提供管道,尽管您必须在管道运行之前构建管道。)
例如,在bash实现中,可以使用
eval
和PIPESTATUS
。对于Perl,您可以使用results()
from您是否尝试将这一长行脚本移动到单独的脚本?@UjinT34我正在努力最小化对现有crontab的更改,因为有太多的cron。(请参见告诫2,可能是告诫1。)此外,即使压缩到单独的脚本中也无法解决当前电子邮件主题在第一条路径中被切断的问题-我确实需要编辑主题和/或更改发件人的功能。您可以使用crontab-l
列出所有的cronjob,还是将它们存储在其他位置(例如./etc/cron.*/*)?@jww-足够公平。为清晰起见进行了编辑。(两个问题都不是实际错误,而是未能有效解决问题陈述。)@jhnc-对crontab-l
是的。我在结尾提到,我已经考虑将从lsof
中挑选的信息与crontab-l
相结合,但我想不出一种方法来证明这一点。谢谢@jhnc,这让我走上了正确的道路!上面发布了最终的工作脚本解决方案。
SUBJECT: script1 | script2 | script3
FROM: Cron Script3<noreply@myserver.com>
(actual results of the command /path/to/script1 | /path/to/script2 | /path/to/script3)
#!/usr/bin/perl -w
my $pgid=`ps -o pgid= -p $$`;
my $lsofout = `/usr/sbin/lsof -g $pgid`;
my @otherpids = `echo "$lsofout" | awk '\$5 == "1w" { print \$2 }'`;
my @longcmds;
my @shortcmds;
foreach my $pid (@otherpids) {
chomp($pid);
if (my $cmd = `ps -o cmd= -p $pid 2>/dev/null`) {
chomp($cmd);
push @longcmds, $cmd;
next;
}
}
my $cmdline = join (' | ',@longcmds);
foreach my $cmd (@longcmds) {
$cmd =~ s/(\/\S+\/)(\S+)/$2/g;
push @shortcmds, $cmd;
}
my $subj = join('|',@shortcmds);
print "SUBJ:$subj\n";
print "CMDLINE: $cmdline\n";
# and now do some mail stuff
#!/usr/bin/perl -w
# cronmgr.pl -- understand cron emails for once
# usage: 0 */3 * * * cronmgr.pl cd blah\; /path/to/script1 \| /path/to/script2 \| /path/to/script3
# note that ; | & in any cronmgr.pl line must be backslashed to run!
use strict;
use IPC::Cmd qw[can_run run run_forked];
my $CMDLINE = join(' ',@ARGV);
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) =
run( command => $CMDLINE, verbose => 0 ); # verbose = 0 means don't output normally, capture all output
my ($stdout, $stderr);
$stdout = join "", @$stdout_buf;
$stderr = join "", @$stderr_buf;
my $emailsubject;
if( $success ) {
if ($stdout eq '' && $stderr eq '') { # if there's no output, don't send any email!
exit;
}
} else {
print "CMD FAIL!\n$error_message\nSTDERR:\n$stderr";
$emailsubject = "FAIL:$error_message";
}
# etc etc
#!/usr/bin/perl -wT
$ENV{PATH} = '/sensible:/path';
my ($pipeline) = grep /\|\s+$0\s+$ARGV[0]/, `crontab -l`;
$pipeline ||= "oops";
# ... mung $pipeline ...
# ... do mail stuff ...
/path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl
coolermailer /path/to/script1 \| /path/to/script2 \| /path/to/script3