0

在此代码中,我解析一个文件(包含来自 的输出ls -lrt)以获取日志文件的修改日期。然后我将所有日志文件移动到一个新文件夹中,并将它们的修改日期添加到文件名中,然后对所有这些文件进行 tar。

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

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

#!/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");
4

2 回答 2

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不正确的值

于 2012-06-13T10:35:24.013 回答
1

我通常不建议使用 bash 代替 perl,但有时它要短得多

这个问题有两个部分:

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

为 1。)

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

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

./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

这将通过时间戳前缀创建 tar 档案。(假设./logs仅包含具有有效(时间戳)文件名的文件)

当然,上面的 sed 模式不是很好,但清楚地显示了seconds从时间戳中删除 - 所以它是按分钟创建档案。如果想要另一个分组,您可以使用:

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

其他:

  • 仅在 gnu 版本的-printffind 中支持 for find - 在 Linux 中很常见
  • 通常不是直接在“/”中工作的好习惯,例如/scripts,因此我的示例使用./
  • 如果在您的 ./scrips 子树中存在具有相同时间戳的相同文件名,则 mv 将覆盖第一个,例如具有相同时间戳的 ./scripts/a/a.log 和 ./scripts/x/a.log 将重命名为 ./logs/TIMESTAMP-a.log
于 2012-06-13T13:37:08.257 回答