Perl скрипт cron / проблема среды - PullRequest
0 голосов
/ 24 января 2019

Следующий скрипт Perl генерирует файл .xls из текстового файла.Он отлично работает в нашей тестовой среде linux, но генерирует пустую электронную таблицу (.xls) в нашей производственной среде при запуске через cron (cron также работает в тесте.) Ничто не выскакивает у наших системных администраторов с точки зрения настроек уровня системы, которыеможет объяснить это поведение.Внизу сценария в подпрограмме import_data сообщается правильное количество строк, но в электронную таблицу ничего не записывается и на уровне сценария или системного уровня ошибки не возвращаются.Я запускал его через Perl-отладчик, но мои навыки не позволяли в интерактивном режиме наблюдать за тем, как он заполняет файл.Запись cron выглядит следующим образом:

cd <script directory>; cvs2xls input.txt output.xls 2>&1

Буду благодарен за любые советы по отладке, а также за потенциальные настройки системы, которые я могу переслать нашим системным администраторам.

#!/usr/bin/perl
use strict;
use warnings;

use lib '/apps/tu01688/perl5/lib/perl5';

use Spreadsheet::WriteExcel;
use Text::CSV::Simple;

BEGIN {
  unshift @INC, "/apps/tu01688/jobs/mayo-expert";
};

my $infile = shift;
usage()  unless defined $infile && -f $infile;
my $parser = Text::CSV::Simple->new;
my @data = $parser->read_file($infile);
my $headers = shift @data;

my $outfile = shift || $infile . ".xls";
my $subject = shift || 'worksheet';

sub usage {
    print "csv2xls infile [outfile] [subject]\n";
    exit;
}

my $workbook = Spreadsheet::WriteExcel->new($outfile);
my $bold = $workbook->add_format();
$bold->set_bold(1);
import_data($workbook, $subject, $headers, \@data);

# Add a worksheet
sub import_data {
    my $workbook  = shift;
    my $base_name = shift;
    my $colums    = shift;
    my $data      = shift;
    my $limit     = shift || 50_000;
    my $start_row = shift || 1;
    my $worksheet = $workbook->add_worksheet($base_name);
    $worksheet->add_write_handler(qr[\w], \&store_string_widths);
    #$worksheet->add_write_handler(qr[\w]| \&store_string_widths);
    my $w = 1;
    $worksheet->write('A' . $start_row, $colums, ,$bold);
    my $i = $start_row;
    my $qty = 0;
    for my $row (@$data) {
        $qty++;
        if ($i > $limit) {
             $i = $start_row;
             $w++;
             $worksheet = $workbook->add_worksheet("$base_name - $w");
             $worksheet->write('A1', $colums,$bold);
        }
        $worksheet->write($i++, 0, $row);
    }
    autofit_columns($worksheet);
    warn "Converted $qty rows.";
    return $worksheet;
}


###############################################################################
###############################################################################
#
# Functions used for Autofit.
#

###############################################################################
#
# Adjust the column widths to fit the longest string in the column.
#
sub autofit_columns {

    my $worksheet = shift;
    my $col       = 0;

    for my $width (@{$worksheet->{__col_widths}}) {

        $worksheet->set_column($col, $col, $width) if $width;
        $col++;
    }
}


###############################################################################
#
# The following function is a callback that was added via add_write_handler()
# above. It modifies the write() function so that it stores the maximum
# unwrapped width of a string in a column.
#
sub store_string_widths {

    my $worksheet = shift;
    my $col       = $_[1];
    my $token     = $_[2];

    # Ignore some tokens that we aren't interested in.
    return if not defined $token;       # Ignore undefs.
    return if $token eq '';             # Ignore blank cells.
    return if ref $token eq 'ARRAY';    # Ignore array refs.
    return if $token =~ /^=/;           # Ignore formula

    # Ignore numbers
    #return if $token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

    # Ignore various internal and external hyperlinks. In a real scenario
    # you may wish to track the length of the optional strings used with
    # urls.
    return if $token =~ m{^[fh]tt?ps?://};
    return if $token =~ m{^mailto:};
    return if $token =~ m{^(?:in|ex)ternal:};


    # We store the string width as data in the Worksheet object. We use
    # a double underscore key name to avoid conflicts with future names.
    #
    my $old_width    = $worksheet->{__col_widths}->[$col];
    my $string_width = string_width($token);

    if (not defined $old_width or $string_width > $old_width) {
        # You may wish to set a minimum column width as follows.
        #return undef if $string_width < 10;

        $worksheet->{__col_widths}->[$col] = $string_width;
    }


    # Return control to write();
    return undef;
}


###############################################################################
#
# Very simple conversion between string length and string width for Arial 10.
# See below for a more sophisticated method.
#
sub string_width {

    return length $_[0];
}

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Большое спасибо за обширную обратную связь, всем.Я, конечно, от этого отнимаю намного больше, чем вкладываю в это.В любом случае я наткнулся на ответ.В моей папке на perl5 lib я обнаружил, что почему-то библиотеки IO и OLE отсутствуют на производстве.Копирование их из разработки привело к тому, что все работало нормально.Тот факт, что я не смог определить / зафиксировать это с помощью обычных усилий по отладке, а не просто сравнивать списки каталогов из-за раздражения, говорит о том, как много мне еще нужно узнать в этом направлении.Но я уверен, что прекрасная обратная связь, которую я получил, во многом поможет мне в этом.Еще раз спасибо всем.

0 голосов
/ 25 января 2019

Хм .. не кладите цепочечные команды в cron, вместо этого используйте внешний скрипт. В любом случае: некоторые предложения, которые могут вам помочь:

Отладка команд cron

Проверьте почту! По умолчанию cron отправляет любые выходные данные команды пользователю, для которого она выполняет команду. Если нет вывода, не будет почты. Если вы хотите, чтобы cron отправлял почту на другую учетную запись, вы можете установить переменную среды MAILTO в файле crontab, например

MAILTO=user@somehost.tld
1 2 * * * /path/to/your/command

Захватите вывод самостоятельно

1 2 * * *  /path/to/your/command &>/tmp/mycommand.log

, который захватывает stdout и stderr в /tmp/mycommand.log

Посмотрите на бревна; cron регистрирует свои действия через системный журнал, который (в зависимости от ваших настроек) часто идет в /var/log/cron или /var/log/syslog.

При необходимости вы можете отфильтровать операторы cron, например,

grep CRON /var/log/syslog 

Теперь, когда мы рассмотрели основы cron, где находятся файлы и как их использовать, давайте рассмотрим некоторые распространенные проблемы.

Убедитесь, что cron работает

Если cron не запущен, ваши команды не будут запланированы ...

ps -ef | grep cron | grep -v grep

должен получить что-то вроде

root    1224   1  0 Nov16 ?    00:00:03 cron

или

root    2018   1  0 Nov14 ?    00:00:06 crond

Если не перезапустить, то

/sbin/service cron start

или

/sbin/service crond start

Могут быть и другие методы; используйте то, что обеспечивает ваш дистрибутив.

cron запускает вашу команду в ограниченной среде.

Доступные переменные среды, вероятно, будут очень ограниченными. Как правило, вы получите только несколько определенных переменных, таких как $LOGNAME, $HOME и $PATH.

Особо следует отметить, что PATH ограничен /bin:/usr/bin. Подавляющее большинство проблем "мой скрипт cron не работает" вызвано этим ограничительным путем . Если ваша команда находится в другом месте, вы можете решить эту проблему несколькими способами:

  1. Укажите полный путь к вашей команде.

    1 2 * * * /path/to/your/command
    
  2. Укажите подходящий PATH в файле crontab

    PATH=/usr:/usr/bin:/path/to/something/else
    1 2 * * * command 
    

Если вашей команде требуются другие переменные окружения, вы можете определить их и в файле crontab.

cron запускает вашу команду с помощью cwd == $ HOME

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

Последняя команда в моем crontab не запускается

Cron обычно требует, чтобы команды заканчивались новой строкой. Отредактируйте свой crontab; перейдите в конец строки, содержащей последнюю команду, и вставьте новую строку (нажмите ввод).

Проверьте формат crontab

Вы не можете использовать пользовательский crontab в формате / crontab для / etc / crontab или фрагменты в /etc/cron.d и наоборот. Crontab, отформатированный пользователем, не содержит имя пользователя в 6-й позиции строки, в то время как crontab, отформатированный системой, включает имя пользователя и запускает команду от имени этого пользователя.

Я поместил файл в /etc/cron.ndomhourly,daily,weekly,monthly}, и он не запустился

  • Убедитесь, что имя файла не имеет расширения, см. run-parts
  • Убедитесь, что у файла есть разрешения на выполнение.
  • Скажите системе, что использовать при выполнении вашего скрипта (например, поставьте #!/bin/sh вверху)

Cron дата связанных с ошибками

Если ваша дата была недавно изменена в результате обновления пользователя или системы, часового пояса или другого, то crontab начнет работать неправильно и выдает странные ошибки, иногда работающие, иногда нет.Это попытка crontab попытаться «сделать то, что вы хотите», когда время изменится из-под него.Поле «минуты» станет недействительным после смены часа.В этом случае принимаются только звездочки.Перезапустите cron и попробуйте снова, не подключаясь к Интернету (чтобы у даты не было возможности выполнить сброс на один из серверов времени).

Знаки процента, снова

Чтобы подчеркнутьсоветы о знаках процента, вот пример того, что cron делает с ними:

# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz

создаст файл ~ / cron.out, содержащий 3 строки

foo
bar
baz

Это особенно навязчивопри использовании команды date.Обязательно избегайте знаков процента

* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
...