【问题标题】:Do we have an autochomp in Perl?我们在 Perl 中有 autochomp 吗?
【发布时间】:2010-09-12 10:17:21
【问题描述】:

这就是我的 Perl 代码在 monitoring a Unix folder 中的样子:

#!/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 看起来不太好。有什么办法可以做“自动选择”吗?

  • 我是 Perl 新手,可能没有以最佳方式编写此代码。欢迎提出任何改进代码的建议。

【问题讨论】:

  • 记住chomp 可以获取列表也很有用:chomp($foo, $bar, $fubb)

标签: perl string chomp


【解决方案1】:

那么,不要使用 shell。

#! /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 的情况下获得 shell 管道 echo ... | mail ... 的效果意味着我们必须自己做一些管道工作,但与 system(LIST) 一样,好处是不必担心 shell 引用。上面的代码使用了多参数open

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

上面的open fork 一个mail 进程,$fh 连接到它的标准输入。父进程(代码仍在运行touchandmail)扮演echoprint $fh $msg 的角色。调用 close 会刷新句柄的 I/O 缓冲区以及一些额外的缓冲区,因为我们是如何打开它的:

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

【讨论】:

  • 感谢 touchandmail 的建议。我正在考虑为它加入一些错误处理。
  • @Lazer: |- 打开一个管道并将写入文件句柄的所有内容重定向到新进程的标准输入。
  • strftime "%a %b %d %H:%M:%S %Z %Y", localtime 可能会也可能不会替代date。问题是%Z 的时区名称依赖于系统。
  • 无论如何,使用 shell 的计算成本更高。每次检查日期都启动一个新进程有点过分。
【解决方案2】:

更一般地说,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 模块之一,但如果您只是读取文件并希望您的所有行都是 @ 987654325@ed,然后IO::All 应用chomptie 选项非常方便。

另请注意,chomp 技巧在将句柄的全部内容直接转换为标量时不起作用(这正是它的实现方式)。

【讨论】:

    【解决方案3】:

    尝试将其放入函数中:

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

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

    【讨论】:

      【解决方案4】:

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

      例如:

      use DateTime;
      
      my $dt = DateTime->now;
      print $dt->strftime('%Y%m%d%H%M.%S');
      

      【讨论】:

      • 对于日期POSIX 可能更容易/更轻松:use POSIX qw/strftime/; my $date = strftime "%Y%m%d%H%M.%S", localtime;
      【解决方案5】:

      可以使用以下语法在一行中分配和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` );
      

      【讨论】:

      • chomp“返回从所有参数中删除的字符总数”,而不是经过筛选的结果。
      • @gbacon : 这就是为什么我 chomping 一个一个的标量
      • 但是您的循环将从chomp 返回的值推送到@result
      • 小心:如果您在 while 循环或 if 子句的测试部分中选择并分配一个标量,您可能无法得到您期望的结果...
      猜你喜欢
      • 2020-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      • 2018-12-25
      • 1970-01-01
      • 2011-01-05
      • 2010-10-25
      相关资源
      最近更新 更多