Повысить эффективность моего скрипта PowerShell - PullRequest
0 голосов
/ 02 ноября 2018

Приведенный ниже код ищет 400+ номеров из файла list.txt , чтобы определить, существует ли он в каких-либо файлах в указанном пути к папке.

Сценарий очень медленный и еще не завершен, поскольку он не завершился после 25 минут работы. Папка, которую мы ищем, имеет размер 507 МБ (532 369 408 байт) и содержит 1,119 файлов & 480 папок . Любая помощь для повышения скорости поиска и эффективности очень ценится.

$searchWords = (gc 'C:\temp\list.txt') -split ','
$results = @()
Foreach ($sw in $searchWords)
{
    $files = gci -path 'C:\Users\david.craven\Dropbox\Asset Tagging\_SJC Warehouse_\_Project Completed_\2018\A*' -filter "*$sw*" -recurse

    foreach ($file in $files)
    {
        $object = New-Object System.Object
        $object | Add-Member -Type NoteProperty –Name SearchWord –Value $sw
        $object | Add-Member -Type NoteProperty –Name FoundFile –Value $file.FullName
        $results += $object
    }

}

$results | Export-Csv C:\temp\output.csv -NoTypeInformation

Ответы [ 3 ]

0 голосов
/ 02 ноября 2018

Таким образом, в опубликованном вами коде PowerShell, безусловно, есть некоторые базовые вещи, которые можно улучшить, но они все еще могут быть не очень быстрыми. Исходя из примера, который вы нам дали, я предполагаю, что вы хотите сопоставить имена файлов со списком слов. Вы просматриваете список слов (400 итераций) и в каждом цикле вы просматриваете все 1119 файлов. Это всего 447 600 итераций!

Предполагая, что вы не можете уменьшить количество итераций в цикле, давайте начнем с ускорения каждой итерации. Командлет Add-Member будет очень медленным, поэтому переключитесь на него, приведя хеш-таблицу к ускорителю типа [PSCustomObject]:

[PSCustomObject]@{
    SearchWord = $Word
    File       = $File.FullName
}

Кроме того, нет необходимости предварительно создавать объект массива, а затем добавлять в него каждый файл. Вы можете просто записать выход цикла foreach в переменную:

$Results = Foreach ($Word in $Words)
{
...

Так что более быстрый цикл может выглядеть так:

$Words = Get-Content -Path $WordList
$Files = Get-ChildItem -Path $Path -Recurse -File

$Results = Foreach ($Word in $Words)
{    
    foreach ($File in $Files)
    {
        if ($File.BaseName -match $Word)
        {
            [PSCustomObject]@{
                SearchWord = $Word
                File       = $File.FullName
            }
        }
    }
}

Более простым подходом может быть использование Where-Object в массиве файлов:

$Results = Foreach ($Word in $Words)
{
    $Files | Where-Object BaseName -match $Word
}

Попробуйте оба варианта и проверьте производительность.

0 голосов
/ 02 ноября 2018

Так что если ускорение цикла не соответствует вашим потребностям, попробуйте полностью удалить цикл. Вы можете использовать регулярное выражение и объединить все слова вместе:

$Words = Get-Content -Path $WordList
$Files = Get-ChildItem -Path $Path -Recurse -File
$WordRegex = $Words -join '|'
$Files | Where basename -match $WordRegex
0 голосов
/ 02 ноября 2018

Следующее должно существенно ускорить вашу задачу:

Если намерение действительно искать искомые слова в именах файлов :

$searchWords = (Get-Content 'C:\temp\list.txt') -split ','
$path = 'C:\Users\david.craven\Dropbox\Facebook Asset Tagging\_SJC Warehouse_\_Project Completed_\2018\A*'

Get-ChildItem -File -Path $path -Recurse -PipelineVariable file |
  Select-Object -ExpandProperty Name |
    Select-String -List -SimpleMatch -Pattern $searchWords |
      Select-Object @{n='SearchWord'; e={$_.Pattern}},
                    @{n='FoundFile'; e={$file.FullName}} |
        Export-Csv C:\temp\output.csv -NoTypeInformation

Если целью является поиск поисковых слов в файлах содержание :

$searchWords = (Get-Content 'C:\temp\list.txt') -split ','
$path = 'C:\Users\david.craven\Dropbox\Facebook Asset Tagging\_SJC Warehouse_\_Project Completed_\2018\A*'

Get-ChildItem -File -Path $path -Recurse |
  Select-String -SimpleMatch -Pattern $searchWords |
    Select-Object @{n='SearchWord'; e={$_.Pattern}},
                  @{n='FoundFile'; e={$_.Path}} |
      Export-Csv C:\temp\output.csv -NoTypeInformation

Ключи к улучшению производительности:

  • Выполните поиск с помощью команды single , передав all слова поиска в Select-String.

  • Вместо создания пользовательских объектов в блоке сценария с New-Object и Add-Member, давайте Select-Object создадим объекты для вас непосредственно в конвейере, используя вычисленные свойства .

  • Вместо итеративного построения промежуточного массива с += - который за сценой каждый раз воссоздает массив - используйте один конвейер для прямой передачи объектов результата в Export-Csv.

...