получить полную текущую строку crontab и создать уникальные заголовки результатов электронной почты (от, тема и т. д.) для каждой строки - PullRequest
2 голосов
/ 20 марта 2019

У меня есть гигантский crontab со многими скриптами на машине с Linux. Мне нужно иметь возможность: а) изменить тему и / или электронную почту с результатом cronjob, потому что по умолчанию это нечитаемо длинный. б) сделать это через централизованное решение. c) Требуются только минимальные изменения в самом crontab.

Например, эта строка crontab:

0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3

Создает тему электронного письма:

Cron <cronuser@myserver> /path/to/script1 | /path/to/script2 | /path/to/script3

Который в моем почтовом ящике обрезается где-то на пути к script1 (И многие строки в crontab значительно длиннее.)

Опции, которые я пробовал:

  1. Отправка по почте, установка темы и т. Д. В строке (-E сохраняет поведение cron по умолчанию только при отправке на выходе):

    0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | mail -E -s "test subject" -S from="Cron Script2 <cronuser@myserver.com>" recipient@myserver.com
    

    Это "работает", но я хочу централизовать свои изменения в одном месте и минимизировать, сколько я добавляю к каждой строке cron для удобства чтения

  2. Использование команды shell: (noop), которая отображается первой в теме (обратите внимание, что пробел после: важен!):

    0 */3 * * *  : Descriptive Words; /path/to/script1 | /path/to/script2 | /path/to/script3
    

    К сожалению, все еще слишком много ненужного, что cron помещает перед "Descriptive Words" в строке темы, так что это все еще невозможно.

Что я хочу создать:

Что-то общее, как это:

0 */3 * * * /path/to/script1 | /path/to/script2 | /path/to/script3 2>&1 | coolmailer.pl

coolmailer.pl будет создавать строку темы, получая команды в этой строке cron, вычеркивая пути и аргументы, и отправляя мне по электронной почте это (опционально только в случае сбоя в любом из сценариев):

SUBJECT: script1 | script2 | script3
FROM: Cron Script3<noreply@myserver.com>
(actual results of the command /path/to/script1 | /path/to/script2 | /path/to/script3)

В качестве бонуса я также хотел бы сказать, была ли какая-либо из предыдущих команд (script1 или script2) не выполнена в строке темы.

Это оказалось ... намного сложнее, чем я ожидал.

Задача:

  1. Найдите способ для члена конвейера (coolmailer) узнать другого участники трубопровода.

Здесь есть гениальный метод для 1, использующий lsof how-do-you-определите фактическую команду-что-то-piping-to-you , но иногда он также находит команды, запущенные командой Сценарии в моем конвейере (т. е. если script1 разветвляет процессы или выполняет системные вызовы, они также появляются каждый раз, когда для их завершения требуется достаточно много времени.) То же самое для метода, использующего группы процессов по той же ссылке.

  1. Найдите способ для члена конвейера (coolmailer) узнать результаты другие участники трубопровода. (Я понимаю, что это может быть невозможно вообще, но взлом lsof дает мне надежду.)

Есть ли лучший способ? Купит ли меня что-нибудь из того, что я бегу из cron? Часть меня хочет объединить стратегию lsof с просмотром результатов crontab -l, но это кажется слишком глупым и подверженным ошибкам.

Предостережения:

  1. Я могу внести изменения в свой аккаунт, но не могу внести изменения, которые будет влиять на всех пользователей. То есть если есть способ изменить Крона формат рассылки для всего сервера, это не помогает.
  2. Я не могу реально обновить каждый вызываемый скрипт для обработки своих результатов по электронной почте, даже если это, вероятно, "правильный" способ.
  3. Я знаю о параметрах mail -s -E -S, но предпочел бы иметь одно место, чтобы что-то менять. Кроме того, я действительно хочу найти способ получить конвейер.
  4. Язык, используемый сейчас для "coolmailer" - это Perl, но я попробую что-нибудь

Моя первая попытка:

(который работает, за исключением того, что он часто также показывает другие команды, запущенные внутри моих скриптов, что означает, что он не работает)

#!/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

И окончательная версия, основанная на предложении Jhnc

#!/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

(Отредактировано для ясности относительно целей и причин, по которым опробованные варианты пока недостаточны.)

1 Ответ

1 голос
/ 21 марта 2019

определение конвейера

Если вы всегда вызываете coolmailer.pl с уникальным аргументом, вы можете просто извлечь его из списка cronjobs:

#!/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

на:

coolermailer /path/to/script1 \| /path/to/script2 \| /path/to/script3

, тогда вы можете построить конвейер вручную и иметь возможность контролировать информацию о состоянии элемента канала.(Это также дает вам конвейер напрямую, хотя вам нужно будет создать его до того, как он запустится.)

Например, при реализации bash вы можете использовать eval и PIPESTATUS.С Perl вы можете использовать results() из IPC :: Run

...