Удалите известные пароли Excel с помощью PowerShell

У меня есть этот код PowerShell, который обрабатывает файлы Excel в указанном каталоге; ссылается на список известных паролей, чтобы найти правильный; а затем открывает, расшифровывает и сохраняет этот файл в новый каталог.

Но он не выполняется так быстро, как хотелось бы (это часть более крупного процесса ETL, и это узкое место). В этот момент я могу удалить пароли быстрее вручную, так как скрипт занимает ~ 40 минут, чтобы расшифровать 40 книг, ссылаясь на список из 50 паролей.

Есть ли командлет или функция (или что-то еще), которая отсутствует, что ускорит это, пропущенный недостаток в обработке или PowerShell, возможно, просто не правильный инструмент для этой работы?

Оригинальный код (обновленный код можно найти ниже):

$ErrorActionPreference = "SilentlyContinue" CLS # Paths $encrypted_path = "C:\PoShTest\Encrypted\" $decrypted_Path = "C:\PoShTest\Decrypted\" $original_Path = "C:\PoShTest\Originals\" $password_Path = "C:\PoShTest\Passwords\Passwords.txt" # Load Password Cache $arrPasswords = Get-Content -Path $password_Path # Load File List $arrFiles = Get-ChildItem $encrypted_path # Create counter to display progress [int] $count = ($arrfiles.count -1) # Loop through each file $arrFiles| % { $file = get-item -path $_.fullname # Display current file write-host "Processing" $file.name -f "DarkYellow" write-host "Items remaining: " $count `n # Excel xlsx if ($file.Extension -eq ".xlsx") { # Loop through password cache $arrPasswords | % { $passwd = $_ # New Excel Object $ExcelObj = $null $ExcelObj = New-Object -ComObject Excel.Application $ExcelObj.Visible = $false # Attempt to open file $Workbook = $ExcelObj.Workbooks.Open($file.fullname,1,$false,5,$passwd) $Workbook.Activate() # if password is correct - Save new file without password to $decrypted_Path if ($Workbook.Worksheets.count -ne 0) { $Workbook.Password=$null $savePath = $decrypted_Path+$file.Name write-host "Decrypted: " $file.Name -f "DarkGreen" $Workbook.SaveAs($savePath) # Close document and Application $ExcelObj.Workbooks.close() $ExcelObj.Application.Quit() # Move original file to $original_Path move-item $file.fullname -Destination $original_Path -Force } else { # Close document and Application write-host "PASSWORD NOT FOUND: " $file.name -f "Magenta" $ExcelObj.Close() $ExcelObj.Application.Quit() } } } $count-- # Next File } Write-host "`n Processing Complete" -f "Green" 

Обновленный код:

 # Get Current EXCEL Process ID's so they are not affected but the scripts cleanup # SilentlyContinue in case there are no active Excels $currentExcelProcessIDs = (Get-Process excel -ErrorAction SilentlyContinue).Id $a = Get-Date $ErrorActionPreference = "SilentlyContinue" CLS # Paths $encrypted_path = "C:\PoShTest\Encrypted" $decrypted_Path = "C:\PoShTest\Decrypted\" $processed_Path = "C:\PoShTest\Processed\" $password_Path = "C:\PoShTest\Passwords\Passwords.txt" # Load Password Cache $arrPasswords = Get-Content -Path $password_Path # Load File List $arrFiles = Get-ChildItem $encrypted_path # Create counter to display progress [int] $count = ($arrfiles.count -1) # New Excel Object $ExcelObj = $null $ExcelObj = New-Object -ComObject Excel.Application $ExcelObj.Visible = $false # Loop through each file $arrFiles| % { $file = get-item -path $_.fullname # Display current file write-host "`n Processing" $file.name -f "DarkYellow" write-host "`n Items remaining: " $count `n # Excel xlsx if ($file.Extension -like "*.xls*") { # Loop through password cache $arrPasswords | % { $passwd = $_ # Attempt to open file $Workbook = $ExcelObj.Workbooks.Open($file.fullname,1,$false,5,$passwd) $Workbook.Activate() # if password is correct, remove $passwd from array and save new file without password to $decrypted_Path if ($Workbook.Worksheets.count -ne 0) { $Workbook.Password=$null $savePath = $decrypted_Path+$file.Name write-host "Decrypted: " $file.Name -f "DarkGreen" $Workbook.SaveAs($savePath) # Added to keep Excel process memory utilization in check $ExcelObj.Workbooks.close() # Move original file to $processed_Path move-item $file.fullname -Destination $processed_Path -Force } else { # Close Document $ExcelObj.Workbooks.Close() } } } $count-- # Next File } # Close Document and Application $ExcelObj.Workbooks.close() $ExcelObj.Application.Quit() Write-host "`nProcessing Complete!" -f "Green" Write-host "`nFiles w/oa matching password can be found in the Encrypted folder." Write-host "`nTime Started : " $a.ToShortTimeString() Write-host "Time Completed : " $(Get-Date).ToShortTimeString() Write-host "`nTotal Duration : " NEW-TIMESPAN –Start $a –End $(Get-Date) # Remove any stale Excel processes created by this script's execution Get-Process excel -ErrorAction SilentlyContinue | Where-Object{$currentExcelProcessIDs -notcontains $_.id} | Stop-Process 

Если ничего другого, я не вижу одной вопиющей проблемы с производительностью, с которой нужно легко обращаться. Вы открываете новый экземпляр excel для проверки каждого отдельного пароля для каждого документа. 40 книг с 50 паролями означают, что вы открыли 2000 экземпляров Excel по одному.

Вы должны иметь возможность использовать один и тот же, не имея функциональности. Извлеките этот код из своей внутренней петли

 # New Excel Object $ExcelObj = $null $ExcelObj = New-Object -ComObject Excel.Application $ExcelObj.Visible = $false 

а также фрагмент, который закроет процесс. Это также должно быть вне цикла.

 $ExcelObj.Close() $ExcelObj.Application.Quit() 

Если это не поможет, вам придется подумать о какой-то параллельной обработке с заданиями и т. Д. У меня есть базовое решение в ответе CodeReview.SE моего, делающего что-то подобное.

В основном, это то, что он запускает сразу несколько предикатов, где каждый работает на куске документов, который работает быстрее, чем один Excel делает все это. Как и в связанном ответе, я предостерегаю автоматизацию Excel COM с помощью PowerShell. COM-объекты не всегда получают надлежащим образом, и блокировки могут быть оставлены на файлах или процессах.


Вы зацикливаетесь на все 50 паролей независимо от успеха или нет. Это означает, что вы можете найти правильный пароль при первом запуске, но вы по-прежнему будете пытаться использовать другие 49! Установите флаг в цикле, чтобы разбить этот внутренний цикл, когда это произойдет.

Что касается логики пароля, вы говорите, что

На этом этапе я могу удалить пароли быстрее вручную, так как сценарий занимает ~ 40 минут

Почему вы можете сделать это быстрее? Что вы знаете, что сценарий этого не делает. Я не вижу, чтобы вы могли выполнять скрипт, но делали именно то, что он сделал.

С тем, что я вижу, другое предложение будет состоять в том, чтобы сохранить / отследить успешные пароли и связанное имя файла. Таким образом, когда он будет обработан снова, вы должны знать первый пароль, который нужно попробовать .

Это решение использует модули ImportExcel для упрощения работы с файлами Excel и PoshRSJob для многопоточной обработки.

Если у вас их нет, установите их, запустив:

 Install-Module ImportExcel -scope CurrentUser Install-Module PoshRSJob -scope CurrentUser 

Я поднял проблему на странице GemHub модуля ImportExcel, где я предложил решение для открытия зашифрованных файлов Excel. Автор может предложить лучшее решение (и рассмотреть влияние на другие функции в модуле, но это работает для меня). На данный момент вам нужно внести изменения в функцию Import-Excel самостоятельно:

Откройте: C:\Username\Documents\WindowsPowerShell\Modules\ImportExcel\2.4.0\ImportExcel.psm1 и выделите функцию Import-Excel. Заменить:

 [switch]$DataOnly 

С

 [switch]$DataOnly, [String]$Password 

Затем замените следующую строку:

 $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream 

С предлагаемым здесь кодом. Это позволит вам вызвать функцию Import-Excel с параметром -Password .

Затем нам нужно, чтобы наша функция неоднократно пыталась открыть уникальный файл Excel, используя известный набор паролей. Откройте окно PowerShell и вставьте следующую функцию (обратите внимание: эта функция имеет определенный путь вывода по умолчанию, а также выводит пароли в подробном потоке – убедитесь, что никто не просматривает ваше плечо или просто удаляет это, если вы предпочитаете ):

 function Remove-ExcelEncryption { [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [String] $File, [Parameter(Mandatory=$false)] [String] $OutputPath = 'C:\PoShTest\Decrypted', [Parameter(Mandatory=$true)] [Array] $PasswordArray ) $filename = Split-Path -Path $file -Leaf foreach($Password in $PasswordArray) { Write-Verbose "Attempting to open $file with password: $Password" try { $ExcelData = Import-Excel -path $file -Password $Password -ErrorAction Stop Write-Verbose "Successfully opened file." } catch { Write-Verbose "Failed with error $($Error[0].Exception.Message)" continue } try { $null = $ExcelData | Export-Excel -Path $OutputPath\$filename return "Success" } catch { Write-Warning "Could not save to $OutputPath\$filename" } } } 

Наконец, мы можем запустить код для выполнения работы:

 $Start = get-date $PasswordArray = @('dj7F9vsm','kDZq737b','wrzCgTWk','DqP2KtZ4') $files = Get-ChildItem -Path 'C:\PoShTest\Encrypted' $files | Start-RSJob -Name {$_.Name} -ScriptBlock { Remove-ExcelEncryption -File $_.Fullname -PasswordArray $Using:PasswordArray -Verbose } -FunctionsToLoad Remove-ExcelEncryption -ModulesToImport Import-Excel | Wait-RSJob | Receive-RSJob $end = Get-Date New-TimeSpan -Start $Start -End $end 

Для меня, если правильный пароль первым в списке, он работает за 13 секунд против 128 файлов Excel. Если я вызываю функцию в стандартном цикле foreach, она занимает 27 секунд.

Чтобы просмотреть, какие файлы были успешно преобразованы, мы можем проверить свойство output на объектах RSJob (это вывод функции Remove-ExcelEncryption где я сказал ему вернуть «Успех»):

 Get-RSJob | Select-Object -Property Name,Output 

Надеюсь, это поможет.

  • Поиск и замена в Excel без циклирования?
  • Отображать данные на SQL Server каждой строки из файла xls с помощью powershell
  • Выровнять по центру весь рабочий лист
  • Powershell поиск Excel документ для точного соответствия
  • Открытие файла excel с использованием формул «Прерывания нового объекта», использование Invoke-Item не
  • Выполните инструкцию If, относящуюся к таблицам Excel
  • Несанкционированная ошибка в Powershell, но не в VBA
  • Пользовательская функция Power shell в скрипте
  • PowerShell: удалите модуль VBA из файла excel
  • Копирование и вставка диапазона excel между книгами в powershell
  • Возвращение Powershell приводит к Excel через VBA
  • Давайте будем гением компьютера.