perl while循环

时间:2012-06-13 09:27:42

标签: linux perl

在这段代码中,我解析了一个文件(包含ls -lrt的输出),以获取日志文件的修改日期。然后我将所有日志文件移动到一个新文件夹中,并将其修改日期添加到文件名中,而不是制作所有这些文件的tar。

我遇到的问题是在while循环中。因为它正在读取所有文件的数据,所以while循环继续运行15次。我知道代码中存在一些问题,但我无法弄明白。

在while循环中,我将分割ls -lrt条记录以查找日志文件修改日期。 $filels命令的输出,我将其存储在文本文件/scripts/yagya.txt中以获取修改日期。但是while循环执行了15次,因为文件夹中有15个与模式匹配的日志文件。

#!/usr/bin/perl
use File::Find;
use strict;

my @field;
my $filenew;
my $date;
my $file = `ls -lrt /scripts/*log*`;
my $directory="/scripts/*.log";
my $current = localtime;
my $current_time = $current;
$current_time = s/\s+//g;
my $freetime = $current_time;
my $daytime = substr($current_time,0,8);
my $seconddir = "/$freetime/";

system ("mkdir $seconddir");

open (MYFILE,">/scripts/yagya.txt");
print MYFILE "$file";
close (MYFILE);

my $data = "/scripts/yagya.txt";
my $datas = "/scripts/";
my %options = (
    wanted => \&wanted,
    untaint => 1
);

find (\%options, $datas);
sub wanted {
    if (/[._]log\d*$/){
        my $files;
        my @fields;
        my $fields;
        chomp;
        $files=$_;

        open (MYFILE,$data);
        while(<MYFILE>){
            chop;
            s/#.*//;
            next unless /\S/;
            @fields = (split)[5,6,7];
            $fields = join('',@fields), "\n";
        }
        close (MYFILE);

        system ("mv  $files $seconddir$fields$files");
    }
}

system ("tar cvf /$daytime/$daytime.tar.gz /$daytime/*log*");
system ("rm $seconddir*log*");

system ("rm $data");

2 个答案:

答案 0 :(得分:3)

您的代码很难阅读。在您开始测试之前,您似乎已将程序编写为单个大块。这种工作方式很常见,但非常错误。您应该首先实现程序的一小部分并在添加更多功能之前进行测试,再次测试,等等。这样你就不会在一个未经测试的大型程序中同时修复许多问题而不知所措。

如果您将use warnings添加到程序顶部的use strict,这对您也有很大帮助。它有助于捕捉您可能忽略的简单错误。

另外,您是否知道File::Find每次遇到文件时都会调用您的wanted回调子程序?它不会立即传递所有文件。

当您找到与yagya.txt找到的当前文件匹配的记录时,当您应该停止时,问题似乎是您正在读取File::Find文件的所有内容。您需要做的是检查ls输出中的当前记录是否以当前文件的名称结尾。如果你像这样编写循环

while (<MYFILE>) {
  if (/\Q$files\E$/) {
    my @fields = (split)[5,6,7];
    $fields = join('',@fields);
    last;
  }
}

然后$fields将以当前文件的修改日期结束,这就是你想要的。

如果您使用Perl为您阅读文件修改日期,这将容易一千倍。

不是将ls列表写入文件并将其读回,而应该执行类似的操作

use File::stat;

my $mtime = localtime(stat($files)->mtime);

会给你一个像Wed Jun 13 11:25:23 2012这样的字符串。我ls输出中的日期仅包括月份名称,月份日期和时间,例如Jun 8 12:37。这不是非常具体,你或许应该至少包括一年,但要从这个$mtime生成相同的字符串,你可以写

my $fields = join '', (split ' ', $mtime)[1,2,3];

关于你的节目还有很多我可以说的,但我希望现在能让它适合你。


我注意到的另外几件事:

  • $current_time = s/\s+//g应为$current_time =~ s/\s+//g以从当前时间字符串中删除所有空格

  • Sun Jun 3 11:50:54 2012之类的值将减少为SunJun311:53:552012,然后$daytime将采用不正确的值SunJun31

答案 1 :(得分:1)

我通常不建议使用bash而不是perl,但有时它会更短

这个问题有两部分:

  1. 将文件重命名到另一个目录并将时间戳添加到文件名
  2. 按每分钟或每小时,几天......等存档它们。
  3. 表示1。)

     find ./scripts -name \*[_.]log\* -type f  -printf "%p\0./logs/%TY%Tm%Td-%TH%Tk%TM-%f\0" | xargs -0 -L 2 mv
    

    上面将找到名称中包含[_。]日志的所有普通文件,并将它们重命名为带有时间戳前缀的./logs目录。 e.g。

    ./scripts/aaa.log12 get renamed into ./logs/20120403-102233-aaa.log12
    

    2。)归档

    ls logs | sed 's/\(........-....\).*/\1/' | sort -u | while read groupby
    do
        ( cd logs && echo tar cvzf ../$groupby.tgz $groupby* )
    done
    

    这将通过timestamp-prefix创建tar存档。 (假设./logs仅包含具有有效(带时间戳)文件名的文件)

    当然,上面的sed模式并不好,但清楚地显示从时间戳中删除seconds - 因此它按分钟创建存档。如果想要其他分组,您可以使用:

    sed 's/\(........-..\).*/\1/'  - by hours
    sed 's/\(........\).*/\1/' - by days 
    

    其他:

    • 仅在gnu版本的find中支持-printf查找 - Linux中常见的
    • 通常不是直接在'/'工作的好习惯,例如/scripts,因此我的示例使用./
    • 如果./scrips子树中存在具有相同时间戳的相同文件名,则mv将覆盖第一个,例如具有相同时间戳的./scripts/a/a.log和./scripts/x/a.log都将重命名为./logs/TIMESTAMP-a.log