2011-01-11 2 views
2

Мне нужно создать скрипт для поиска через менее миллиона файлов текста, кода и т. Д., Чтобы найти совпадения, а затем выводить все удары по определенному строковому шаблону в файл CSV.Оптимизация сценария простого поиска в PowerShell

До сих пор я делал это;

$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» со списком всех файлов содержащий слово «бар».

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

Все данные оцениваются!

+1

Сколько стоит сейчас (только из любопытства)? Вам нужны только пути к файлам, которые содержат совпадения в выходе? –

+0

Теперь в массиве требуется ~ 2 часа pr. Я только что немного узнал трюк с мерой-командой, я увижу, будет ли производительность увеличиваться по мере кэширования процесса. - Мне нужны только пути к файлам, которые содержат совпадения, да – cc0

+0

Я также могу добавить, что длина каждого элемента массива (строки) значительно влияет на время обработки. Во время первого прогона загрузка процессора составляла около 15-20%. Сейчас это около 4-5%. Интересный материал. – cc0

ответ

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

+0

Удивительный, вы были на 2 секунды быстрее! – stej

+0

:-) Но наши предложения не совсем то же самое. Таким образом, @ cc0 теперь имеет больше возможностей выбора, и это навсегда. –

+0

Это превосходно:] Надеюсь, другие смогут также учиться на этом. Спасибо, что нашли время! – cc0

2

Предположим, что 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" 
} 
+0

Спасибо :) Я тоже дам этот снимок и посмотрю, что быстрее для моей ситуации. Должно быть интересно! – cc0

+0

Было бы здорово увидеть результаты в вашем отредактированном вопросе;) – stej

+0

Я сделаю это, как только у меня их получится:] Может потребоваться несколько дней, я проведу здесь правильное тестирование со множеством предметов. – cc0

Смежные вопросы