0

「Linux カーネル バージョン」または「USB_STATE=DISCONNECTED」に基づいて、既存のファイル名を Kernel.txt にリネームしようとしています。スクリプトはエラーなしで実行されていますが、出力はありません。変更されたファイルは、以前と同じフォルダー (F1、F2、F3) にある必要があります。

Top dir: Log
 SubDir: F1,F2,F3
    F1: .bin file,.txt file,.jpg file
    F2: .bin file,.txt file,.jpg file
    F3: .bin file,.txt file,.jpg file

#!/usr/bin/perl 
use strict;
use warnings;
use File::Find;
use File::Basename;
use File::Spec;
use Cwd;
chdir('C:\\doc\\logs');
my $dir_01 = getcwd;

my $all_file=find ({ 'wanted' => \&renamefile }, $dir_01);
sub renamefile 
{
  if ( -f and /.txt?/ )
  {
   my @files = $_;
   foreach my $file (@files)
  {
    open (FILE,"<" ,$file) or die"Can not open the file";
    my @lines = <FILE>; 
    close FILE;
    for my $line ( @lines ) 
    {
       if($line=~ /Linux kernel Version/gi || $line=~ /USB_STATE=DISCONNECTED/gi)
       {    
         my $dirname = dirname($file); # file's directory, so we rename only the file itself.
         my $file_name = basename($file); # File name fore renaming.
         my $new_file_name = $file_name;
         $new_file_name =~ s/.* /Kernal.txt/g; # replace the name with Kernal.txt
         rename($file, File::Spec->catfile($dirname, $new_file_name)) or die $!; 
        }
     }
   }
  } 
 }
4

1 に答える 1

1

このコードは、カーゴカルト プログラミングに少し似ています。つまり、これが何をしているかを理解していることを示すことなく、いくつかの構成要素がここにあります。


chdir('C:\\doc\\logs');
my $dir_01 = getcwd;

Windows のパス名であっても、スラッシュを使用してください。これは一般的にサポートされています。

あなたのディレクトリ図は、トップ dir があることを示してLogいますが、chdir に移動しC:/doc/logsます。それは何ですか?

これ$dir_01は非常にわかりにくい名前であり、chdir したばかりのパスであることに気付きましたか? また、File::Find作業ディレクトリで開始する必要はありません。つまり、chdirここでは少し役に立ちません。あなたは実際に欲しい:

my $start_directory = "C:/doc/Log"; # or whatever

my $all_file=find ({ 'wanted' => \&renamefile }, $dir_01);

findの戻り値が何を意味するのかわかりません。しかし、それを未使用の変数に入れる必要はないと確信しています。

太いコンマでキー名を提供する場合=>、これらのキーを手動で引用する必要はありません。したがって:

find({ wanted => \&renamefile }, $start_directory);

/.txt?/

この正規表現は次のことを行います。

  • 任意の文字 (改行ではない) に一致します。
  • その後にリテラルtx
  • オプションで a t. the?は 0 または 1 の量指定子です。

で終わるファイル名を一致させたい場合は.txt、次のようにする必要があります

/\.txt$/

\.文字どおりのピリオドに一致します。文字列の$末尾に正規表現を固定します。


my @files = $_;
foreach my $file (@files) {
  ...;
}

これは通常、次のように記述されます。

my $file = $_;
...;

の値を配列に代入する$_と、配列には1 つの要素 (コンテンツ) が含まれます。次に、この1 つの要素をループします。このようなループは、ループと呼ぶに値しません。@files$_


open (FILE,"<" ,$file) or die"Can not open the file";
my @lines = <FILE>; 
close FILE;
for my $line ( @lines ) 
{ ... }

ああ、どこから始めますか?

  1. ファイル ハンドルにはレキシカル変数を使用します。これらには、自分自身を閉じるという優れた特性があります。

  2. エラー処理については、use autodie. 本当に自分でやりたい場合は、エラー メッセージに次の 2 つの重要な情報が含まれている必要があります。

    • 開けなかったファイルの名前 ( $file)
    • オープンに失敗した理由 ( $!)

    のような意味になり... or die "Can't open $file: $!"ます。

  3. ファイル全体を配列に読み取って、それをループしないでください。代わりに、 -like ループを使用して、メモリ効率を高め、行を反復処理します。while(<>)これは一度に 1 行しか読み取れないため、はるかに優れています。

組み合わせるとこんな感じ

use autodie; # at the top

open my $fh, "<", $file;
LINE: while (<$fh>) {
  ...; # no $line variable, let's use $_ instead
}

LINEああ、あとで参照できるように、ループに ( を付けて) ラベルを付けました。


if($line=~ /Linux kernel Version/gi || $line=~ /USB_STATE=DISCONNECTED/gi) { ... }

フラグを正規表現に置くと、/g正規表現が反復子になります。あなたは本当にそれを望んでいません。そして、大文字と小文字を区別しないマッチングが本当に必要かどうかはよくわかりません。||正規表現の代替を使用して、またはを正規表現に移動できます|。行を含めるために使用するようになっ$_たため、正規表現を文字列に手動でバインドする必要はありません。したがって、次のように書くことができます。

if (/Linux Kernel Version|USB_STATE=DISCONNECTED/i) { ... }

my $dirname = dirname($file); # file's directory, so we rename only the file itself.
my $file_name = basename($file); # File name fore renaming.

デフォルトでは、元$_の 、したがって私たち$fileの にはファイル名のみが含まれ、ディレクトリは含まれません。これは問題ではありません: File::Findchdirは正しいディレクトリに移動します。これにより、処理がはるかに簡単になります。ディレクトリが必要な場合は、$File::Find::dir変数を使用します。


my $new_file_name = $file_name;
$new_file_name =~ s/.* /Kernal.txt/g;

/.* /正規表現は次のように述べています。

  • 最後のスペースを含めて何にでもマッチ
  • これが一致する場合は、一致した部分を に置き換えKernal.txtます。

ここ/gではフラグはまったく役に立ちません。eKernel.txtを使いたくないですか?そして、なぜファイル名にスペースがあるのですか? よくわかりません。ファイルの名前を に変更したい場合は置換で奇妙なことをする代わりに、それを文字列として割り当てるだけです: Kernel.txt

my $new_file_name = "Kernel.txt";

rename($file, File::Spec->catfile($dirname, $new_file_name)) or die $!; 

エラー メッセージにファイル名も含める必要があること、またはさらに良いこと: 自動エラー処理を使用する必要があることは既に確認しました。

また、すでに正しいディレクトリにいるため、新しい名前をディレクトリと連結する必要はありません。

rename $file => $new_file_name; # error handling by autodie
last LINE;

それで十分なはずです。また、 LINEループを離れることに注意してください。ファイルの名前を変更したら、他の行も確認する必要はありません。

于 2013-07-25T06:11:34.573 に答える