Извлечение памяти из Powershell - PullRequest
0 голосов
/ 23 мая 2019

Я работал над небольшим проектом, который должен извлечь некоторую информацию из файлового сервера. Для выполнения этого проекта я создал скрипт, который выводит всю информацию в файл .csv. Проблема в том, что Powershell в процессе потребляет всю оперативную память моего компьютера, потому что для анализа требуется сотни ГБ данных.

Вот мой сценарий.

$folder = Get-ChildItem -Recurse 'Complete_Path' | select FullName, @{Name="Owner";Expression={(Get-Acl $_.FullName).Owner}}, CreationTime, LastWriteTime, LastAccessTime, PSIsContainer | sort FullName
$output = @()

$folder | foreach {

$type =

if ($_.PSIsContainer -eq "True") {


    Write-Output "Folder"

        }
else {


    Write-Output "File"

}


$size =

if ($_.PSIsContainer -eq "True") {

   Get-ChildItem -Recurse $_.FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum


        }
else {


    Get-Item $_.FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum

}


$hash = @{


FullName = $_.FullName
Owner = $_.Owner
CreationTime = $_.CreationTime
LastWriteTime = $_.LastWriteTime
LastAccessTime = $_.LastAccessTime
Type = $type
'Size in MB' = [math]::Round($($size/1Mb),2)

}

$output += New-Object PSObject -Property $hash
}

$output | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime, Type, 'Size in MB' | Export-Csv C:\myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8

Ребята, вы понимаете, как я могу выполнить работу быстрее и с меньшими затратами оперативной памяти? На добычу может уйти несколько дней.

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 23 мая 2019

Разве это лучше, когда все в одном конвейере?

Get-ChildItem -Recurse |
select FullName, @{Name="Owner";Expression={(Get-Acl $_.FullName).Owner}},
CreationTime, LastWriteTime, LastAccessTime, PSIsContainer | sort FullName |

foreach {

  $type =
  if ($_.PSIsContainer -eq "True") {
      Write-Output "Folder"
  }
  else {
      Write-Output "File"
  }

  $size =
  if ($_.PSIsContainer -eq "True") {
     Get-ChildItem -Recurse $_.FullName | 
       measure -Property Length -Sum -ErrorAction SilentlyContinue |
       select -ExpandProperty Sum
  }
  else {
      Get-Item $_.FullName | 
        measure -Property Length -Sum -ErrorAction SilentlyContinue |
        select -ExpandProperty Sum
  }

  $hash = @{
    FullName = $_.FullName
    Owner = $_.Owner
    CreationTime = $_.CreationTime
    LastWriteTime = $_.LastWriteTime
    LastAccessTime = $_.LastAccessTime
    Type = $type
    'Size in MB' = [math]::Round($($size/1Mb),2)
  }

  New-Object PSObject -Property $hash
} | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime,
Type, 'Size in MB' | 
Export-Csv myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8
0 голосов
/ 23 мая 2019
  • Замените массив Powershell $output=@() на список .Net PSObject $output = [System.Collections.Generic.List[psobject]]::new() и используйте метод .Add для этого объекта, чтобы добавить свои элементы.

    Для небольшого списка вы не заметите, но использование массива Powershell и оператора + = значительно снижает производительность. Каждый раз, когда вы делаете + =, массив воссоздается полностью с еще одним элементом.

  • Включить Length в ваш начальный оператор Get-ChildItem. Позже вы можете измерить сумму, не повторяя процедуру Get-ChildItem все время

  • Конвейер играет хорошо с памятью, но в целом медленнее. Я предпочитаю не использовать конвейер, когда производительность становится проблемой.

что-то подобное уже должно быть значительно быстрее

$folder = Get-ChildItem -Recurse "$($env:USERPROFILE)\Downloads" | select FullName, @{Name = "Owner"; Expression = { (Get-Acl $_.FullName).Owner } }, CreationTime, LastWriteTime, LastAccessTime, PSIsContainer, Length | sort FullName
$output = [System.Collections.Generic.List[psobject]]::new()

foreach ($Item in $folder) {
    if ($Item.PSIsContainer) {
        $Type = 'Folder'
        $size = $folder.Where( { $_.FullName -like $item.FullName }).FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum
    }
    else {
        $Type = 'File'
        $size = $Item.Length
    }
    $size = [math]::Round($($size / 1Mb), 2)

    $hash = @{
        FullName       = $Item.FullName
        Owner          = $Item.Owner
        CreationTime   = $Item.CreationTime
        LastWriteTime  = $Item.LastWriteTime
        LastAccessTime = $Item.LastAccessTime
        Type           = $Type
        'Size in MB'   = $size
    }
    [void]($output.Add((New-Object PSObject -Property $hash)))
}


$output | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime, Type, 'Size in MB' | Export-Csv C:\myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8
  • Вы все еще можете улучшить вычисление размера, так что сначала рассчитывается самый глубокий размер папок, затем родительская папка может получить значение и суммировать дочернюю папку вместо пересчета файлов

  • Другая мысль состояла бы в том, чтобы не выполнять Get-ACl немедленно (я подозреваю, что это выполняется медленно) и получать ваши элементы, делать все остальное, а затем распараллеливать Get-ACL, чтобы вы могли получить значения на количество параллельных потоков и добавьте значение, как вы получите его в свой список.

Подумайте о тестировании вашего кода небольшими партиями и используйте команду Measure-Command, чтобы определить, где самая медленная операция в вашем коде.

Я рекомендую вам взглянуть на более продвинутую тему по этому вопросу. Вот хорошая статья для начала: Slow Code: 5 лучших способов заставить ваши скрипты Powershell работать быстрее

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...