У нас есть автохомп в Perl? - PullRequest
       12

У нас есть автохомп в Perl?

8 голосов
/ 12 сентября 2010

Так выглядит мой Perl-код для мониторинга папки Unix :

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec::Functions;

my $date    = `date`; chomp $date;
my $datef   = `date +%Y%m%d%H%M.%S`; chomp $datef;
my $pwd     = `pwd`; chomp $pwd;

my $cache   = catfile($pwd, "cache");
my $monitor = catfile($pwd, "monme");
my $subject = '...';
my $msg     = "...";
my $sendto  = '...';
my $owner   = '...';

sub touchandmail {
     `touch $cache -t "$datef"`;
     `echo "$msg" | mail -s "$subject" $owner -c $sendto`;
}

while(1) {

    $date  = `date`; chomp $date;
    $datef = `date +%Y%m%d%H%M.%S`; chomp $datef; 

    if (! -e "$cache") {
        touchandmail();
    } elsif ("`find $monitor -newer $cache`" ne "") {
        touchandmail();
    }
    sleep 300;
}
  • Делать chomp после каждого задания выглядит не очень хорошо. Есть ли какой-нибудь способ сделать "autochomp"?

  • Я новичок в Perl и, возможно, написал этот код не лучшим образом. Любые предложения по улучшению кода приветствуются.

Ответы [ 5 ]

14 голосов
/ 12 сентября 2010

Тогда не используйте оболочку.

#! /usr/bin/perl

use warnings;
use strict;

use Cwd;
use POSIX qw/ strftime /;

my $date    = localtime;
my $datef   = strftime "%Y%m%d%H%M.%S", localtime;
my $pwd     = getcwd;

Результат немного отличается: выходные данные команды date содержат часовой пояс, но значение $date выше не будет. Если это проблема, следуйте прекрасному предложению Час. Owens ниже и используйте strftime, чтобы получить нужный формат.

Ваш саб

sub touchandmail {
  `touch $cache -t "$datef"`;
  `echo "$msg" | mail -s "$subject" $owner -c $sendto`;
}

потерпит молчание, если что-то пойдет не так. Тихие неудачи - это мерзости. Лучше было бы код по линии

sub touchandmail {
  system("touch", "-t", $datef, $cache) == 0
    or die "$0: touch exited " . ($? >> 8);

  open my $fh, "|-", "mail", "-s", $subject, $owner, "-c", $sendto
    or die "$0: could not start mail: $!";

  print $fh $msg
    or warn "$0: print: $!";

  unless (close $fh) {
    if ($! == 0) {
      die "$0: mail exited " . ($? >> 8);
    }
    else {
      die "$0: close: $!";
    }
  }
}

Использование system вместо обратных кавычек более выразительно для вашего намерения, потому что обратные пометки предназначены для захвата выходных данных. Форма system(LIST) обходит оболочку и беспокоится о цитировании аргументов.

Получение эффекта конвейера оболочки echo ... | mail ... без оболочки означает, что нам нужно немного проделать сантехническую работу, но выгода - как и в system(LIST) - не нужно беспокоиться о цитировании оболочки. В приведенном выше коде используется множество аргументов open:

Для трех или более аргументов, если MODE равен '|-', имя файла интерпретируется как команда, к которой должен быть передан вывод, а если MODE равно '-|', имя файла интерпретируется как команда, передающая нам выходные данные , В форме с двумя аргументами (и с одним аргументом) следует заменить тире ('-') на команду. См. Использование open для IPC в perlipc для получения дополнительных примеров этого.

Приведенный выше open разветвляет процесс mail, и $fh подключается к его стандартному входу. Родительский процесс (код все еще выполняется touchandmail) выполняет роль echo с print $fh $msg. Вызов close очищает буферы ввода / вывода ручки и немного больше из-за того, как мы ее открыли:

Если файловый дескриптор получен из канала open, close возвращает false, если один из других задействованных системных вызовов завершается ошибкой или если его программа завершает работу с ненулевым состоянием. Если единственная проблема состояла в том, что программа вышла не из нуля, $! будет установлен в 0. Закрытие канала также ожидает завершения процесса, выполняющегося в канале - в случае, если вы захотите посмотреть на вывод канала впоследствии. - и неявно помещает значение состояния выхода этой команды в $? и ${^CHILD_ERROR_NATIVE}.

6 голосов
/ 12 сентября 2010

В более общем смысле, модуль IO::All действительно обеспечивает эквивалент autochomp:

use IO::All;
# for getting command output:
my @date = io("date|")->chomp->slurp;
#$date[0] contains the chomped first line of the output

или, в более общем случае:

my $fh = io("file")->chomp->tie;
while (<$fh>) {
 # no need to chomp here !  $_ is pre-chomped
}

Конечно, для этого конкретного случая date Я согласен с другими ответчиками, что вам, вероятно, лучше использовать один из модулей DateTime, но если вы просто читаете в файле и хотите, чтобы все ваши строки были chomp ed, затем IO::All с опциями chomp и tie очень удобно.

Обратите также внимание, что трюк chomp не работает, когда все содержимое дескриптора вбрасывается в скаляр напрямую (именно так оно и реализовано).

5 голосов
/ 12 сентября 2010

Попробуйте ввести его в функцию:

sub autochomp {
    my $command = shift;
    my $retval = `$command`;
    chomp $retval;
    return $retval;
}

И затем вызывайте это для каждой команды, которую вы хотите выполнить, а затем chomp.

4 голосов
/ 12 сентября 2010

Используйте DateTime или другие модули даты в CPAN вместо утилиты даты.

Например:

use DateTime;

my $dt = DateTime->now;
print $dt->strftime('%Y%m%d%H%M.%S');
2 голосов
/ 12 сентября 2010

Можно назначить и chomp в одной строке, используя следующий синтаксис:

chomp ( my $date = `date` );

Что касается более быстрого разговора, если вы обнаружите, что повторяете одно и то же снова и снова, бросьтеэто в подпункте:

sub assign_and_chomp {

    my @result;
    foreach my $cmd (@_) {
        chomp ( my $chomped = $cmd );
        push @result, $chomped;
    }
    return @result;
}

my ( $date , $datef , $pwd )

   = assign_and_chomp ( `date` , `date +%Y%m%d%H%M.%S` , `pwd` );
...