Оптимизация простого поискового скрипта в PowerShell - PullRequest
2 голосов
/ 11 января 2011

Мне нужно создать скрипт для поиска чуть менее миллиона файлов текста, кода и т. Д., Чтобы найти совпадения, а затем вывести все совпадения по определенному строковому шаблону в файл 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"
}

Это возвращает мне CSV-файл с именем "foo.txt" со списком всех файлов со словом "foo" в нем и файл с именем "bar.txt"со списком всех файлов, содержащих слово «bar».

Можно ли как-нибудь придумать, как оптимизировать этот скрипт, чтобы он работал быстрее?Или идеи о том, как сделать совершенно другой, но эквивалентный скрипт, который будет работать быстрее?

Весь вклад приветствуется!

Ответы [ 2 ]

2 голосов
/ 11 января 2011

Если ваши файлы не очень большие и их можно прочитать в память, тогда эта версия должна работать довольно быстро (и мой быстрый и грязный локальный тест, похоже, доказывает это):

$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 нет особой причины, если вас интересуют только пути - для простых текстовых файлов подойдет один путь на строку.

2 голосов
/ 11 января 2011

Предположим, что 1) файлы не слишком велики, и вы можете загрузить их в память, 2) вам просто нужен путь к файлу, который соответствует (не строка и т. Д.).

Я попытался прочитать файл только один раз, а затем перебрать регулярные выражения.Есть некоторый выигрыш (это быстрее, чем оригинальное решение), но конечный результат будет зависеть от других факторов, таких как размеры файлов, количество файлов и т. Д.

Кроме того, удаление '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"
}
...