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 を使用してファイルの変更日を読み取ると、これは 1000 倍簡単になります。

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];

あなたのプログラムについて私が言えることはまだまだたくさんありますが、今のところはこれでうまくいくことを願っています。


私が気づいたもう2つのこと:

  • 行は、現在の時刻文字列からすべてのスペースを削除する$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

通常、perl の代わりに bash を使用することはお勧めしませんが、はるかに短い場合もあります。

この問題には 2 つの部分があります。

  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 

他の:

  • -printffor find は gnu バージョンの find でのみサポートされています - Linux では一般的です
  • 通常、 のように「/」で直接作業することはお勧めできません/scripts。したがって、私の例では./
  • ./scrips サブツリーに同じタイムスタンプを持つ同じファイル名が存在する場合、mv は最初のものを上書きします。たとえば、同じタイムスタンプを持つ ./scripts/a/a.log と ./scripts/x/a.log の両方が./logs/TIMESTAMP-a.log に名前を変更する
于 2012-06-13T13:37:08.257 に答える