8

这是我的 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 新手,可能没有以最好的方式编写此代码。欢迎任何改进代码的建议。

4

5 回答 5

14

那就别用壳了。

#! /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上面的值不会。如果这是一个问题,请遵循Chas 的出色建议。下面的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)表单绕过了外壳,不必担心引用参数。

在没有 shell 的情况下获得 shell 管道的效果echo ... | mail ...意味着我们必须自己做一些管道工作,但好处——就像这样system(LIST)——不必担心 shell 引用。上面的代码使用多参数open

对于三个或更多参数,如果 MODE 为'|-',则文件名被解释为要通过管道输出的命令,如果 MODE 为'-|',则文件名被解释为通过管道输出给我们的命令。在双参数(和单参数)形式中,应将破折号 ( '-') 替换为命令。有关这方面的更多示例,请参阅在 perlipc 中使用openIPC 。

上面的openfork 一个mail进程,并$fh连接到它的标准输入。父进程(代码仍在运行touchandmail)执行echowith的角色print $fh $msgclose由于我们打开它的方式,调用会刷新句柄的 I/O 缓冲区以及一些额外的缓冲区:

如果文件句柄来自 piped openclose则在涉及的其他系统调用之一失败或其程序以非零状态退出时返回 false。如果唯一的问题是程序以非零值退出,$!则将设置为 0。关闭管道还会等待在管道上执行的进程退出——以防你希望在之后查看管道的输出——并且隐式地将该命令的退出状态值放入$?and${^CHILD_ERROR_NATIVE}中。

于 2010-09-12T11:01:08.440 回答
6

更一般地说,该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编辑所有行,那么IO::All使用chompandtie选项应用非常方便。

另请注意,chomp当将句柄的全部内容直接吞入标量时,该技巧不起作用(这就是它的实现方式)。

于 2010-09-12T13:45:07.620 回答
5

尝试将其放入函数中:

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

然后为您要执行的每个命令调用它,然后 chomp。

于 2010-09-12T10:28:06.970 回答
4

在 CPAN 上使用 DateTime 或其他日期模块,而不是 date 实用程序。

例如:

use DateTime;

my $dt = DateTime->now;
print $dt->strftime('%Y%m%d%H%M.%S');
于 2010-09-12T10:33:11.957 回答
2

chomp可以使用以下语法在一行中分配和:

chomp ( my $date = `date` );

至于说得更 Perlishly,如果你发现自己一遍又一遍地重复同样的事情,把它变成一个 sub:

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` );
于 2010-09-12T10:33:38.627 回答