2

テキストやコードなどの 100 万ファイル弱を検索して一致を見つけ、特定の文字列パターンのすべてのヒットを CSV ファイルに出力するスクリプトを作成する必要があります。

これまでのところ、これを作成しました。

$location = 'C:\Work*'

$arr = "foo", "bar" #Where "foo" and "bar" are string patterns I want to search for (separately)

for($i=0;$i -lt $arr.length; $i++) {
Get-ChildItem $location -recurse | select-string -pattern $($arr[$i]) | select-object Path | Export-Csv "C:\Work\Results\$($arr[$i]).txt"
}

これにより、「foo」という単語を含むすべてのファイルのリストを含む「foo.txt」という名前の CSV ファイルと、「bar」という単語を含むすべてのファイルのリストを含む「bar.txt」という名前のファイルが返されます。

このスクリプトを最適化して動作を高速化する方法はありますか? または、まったく異なるが、より高速に動作する同等のスクリプトを作成する方法についてのアイデアはありますか?

すべての入力に感謝します!

4

2 に答える 2

2

1)ファイルが大きすぎず、メモリにロードできる、2)(行などではなく)一致するファイルのパスが本当に必要であると仮定します。

ファイルを1回だけ読み取ってから、正規表現を繰り返し処理しようとしました。ある程度のメリットはありますが(元のソリューションよりも高速です)、最終的な結果は、ファイルサイズ、ファイル数などの他の要因によって異なります。

また、削除'ignorecase'すると少し速くなります。

$res = @{}
$arr | % { $res[$_] = @() }

Get-ChildItem $location -recurse | 
  ? { !$_.PsIsContainer } |
  % { $file = $_
      $text = [Io.File]::ReadAllText($file.FullName)
      $arr | 
        % { $regex = $_
            if ([Regex]::IsMatch($text, $regex, 'ignorecase')) {
              $res[$regex] = $file.FullName
            }
        }
  }
$res.GetEnumerator() | % { 
  $_.Value | Export-Csv "d:\temp\so-res$($_.Key).txt"
}
于 2011-01-11T12:49:38.037 に答える
2

ファイルが大きくなく、メモリに読み込むことができる場合、このバージョンは非常に高速に動作するはずです(そして、私の迅速で汚いローカルテストはそれを証明しているようです):

$location = 'C:\ROM'
$arr = "Roman", "Kuzmin"

# remove output files
foreach($test in $arr) {
    Remove-Item ".\$test.txt" -ErrorAction 0 -Confirm
}

Get-ChildItem $location -Recurse | .{process{ if (!$_.PSIsContainer) {
    # read all text once
    $content = [System.IO.File]::ReadAllText($_.FullName)
    # test patterns and output paths once
    foreach($test in $arr) {
        if ($content -match $test) {
            $_.FullName >> ".\$test.txt"
        }
    }
}}}

注:1)例では、変更されたパスとパターンに注意してください。2)出力ファイルはCSVではなくプレーンテキストです。パスだけに関心がある場合、CSVにはあまり理由がありません。プレーンテキストファイルでは、1行に1つのパスで十分です。

于 2011-01-11T12:49:40.453 に答える