Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/powershell/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用PowerShell删除已知的Excel密码_Excel_Powershell - Fatal编程技术网

使用PowerShell删除已知的Excel密码

使用PowerShell删除已知的Excel密码,excel,powershell,Excel,Powershell,我有一个PowerShell代码,可以在指定目录中的Excel文件中循环;参考已知密码列表以找到正确的密码;然后打开、解密该文件并将其保存到新目录中 但它并没有像我希望的那样快速执行(它是更大的ETL过程的一部分,是一个瓶颈)。此时,我可以更快地手动删除密码,因为脚本需要约40分钟来解密40本工作簿,同时引用约50个密码的列表 是否缺少可以加快此速度的cmdlet或函数(或其他内容),是否存在处理过程中被忽略的缺陷,或者PowerShell可能不是此作业的合适工具 原始代码(更新代码可在下面找到

我有一个PowerShell代码,可以在指定目录中的Excel文件中循环;参考已知密码列表以找到正确的密码;然后打开、解密该文件并将其保存到新目录中

但它并没有像我希望的那样快速执行(它是更大的ETL过程的一部分,是一个瓶颈)。此时,我可以更快地手动删除密码,因为脚本需要约40分钟来解密40本工作簿,同时引用约50个密码的列表

是否缺少可以加快此速度的cmdlet或函数(或其他内容),是否存在处理过程中被忽略的缺陷,或者PowerShell可能不是此作业的合适工具

原始代码(更新代码可在下面找到):

更新代码:

# 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/o a 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实例

您应该能够继续使用相同的一个,而不会受到功能的影响。将此代码从最内部的循环中取出

以及将关闭流程的代码段。它也需要脱离循环

如果你没有足够的帮助,你就不得不考虑做一些与工作相关的并行处理。我有一个基本的解决办法,我的回答是类似的。 基本上,它一次运行几个Excel,其中每个Excel处理一大块文档,这比一个Excel处理所有文档的速度都快。正如我在链接答案中所做的那样,我警告使用PowerShell实现Excel COM的自动化。COM对象并不总是被正确释放,锁可能会留在文件或进程上


无论成功与否,您都在循环获取所有50个密码。这意味着你可以找到正确的密码在第一次去,但你仍然要尝试其他49!在循环中设置一个标志,以在发生这种情况时中断该内部循环

就密码逻辑而言,你是这么说的

此时,我可以更快地手动删除密码,因为脚本大约需要40分钟

为什么你能做得更快?你知道什么是脚本不知道的。我不认为你能比剧本表现得更好,但你能做的正是它所做的


根据我所看到的,另一个建议是保留/跟踪成功的密码和相关文件名。这样,当它再次被处理时,您将知道要尝试的第一个密码

此解决方案使用这些模块,以便更轻松地处理Excel文件和多线程处理

如果没有这些,请通过运行以下命令进行安装:

Install-Module ImportExcel -scope CurrentUser
Install-Module PoshRSJob -scope CurrentUser
我在ImportExcel模块GitHub页面上提出了一个问题,我提出了一个打开加密Excel文件的解决方案。作者可以提出一个更好的解决方案(并且考虑模块中其他功能的影响,但这对我来说是有效的)。现在,您需要自己修改导入Excel函数:

打开:
C:\Username\Documents\WindowsPowerShell\Modules\ImportExcel\2.4.0\ImportExcel.psm1
并滚动到导入Excel函数。替换:

[switch]$DataOnly

然后更换以下行:

$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
使用建议的代码。这将允许您使用
-Password
参数调用
导入Excel
函数

接下来,我们需要我们的函数反复尝试使用一组已知的密码打开一个单一的Excel文件。打开PowerShell窗口并粘贴到以下函数中(注意:此函数定义了默认输出路径,并在详细流中输出密码-确保没有人监视您,或者如果您愿意,只需将其删除):

最后,我们可以运行代码来完成这项工作:

$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秒

要查看成功转换的文件,我们可以检查RSJob对象上的
output
属性(这是我告诉它返回“Success”的
Remove ExcelEncryption
函数的输出):


希望这会有所帮助。

如果这段代码确实有效,但可以提高性能,那么它将是CodeReview.SE的一个很好的候选者。当然,这也是本文的主题。您可以尝试使用作业或创建运行空间来同时处理多个文件:这将大大提高速度。保存成功的解密操作文件名和密码关联是否有益?这样后续的运行就可以先尝试一些东西了?唯一有用的是名字是从布鲁夫的想法延续下来的。在此之前,尽管您正在为每次密码检查创建一个新的excel实例。我会尝试使用一个用于所有检查,只需对导入Excel进行一点修改(等待Github存储库的修复),使用PoshRSJob,我能够在3.3分钟内从128个excel文件中删除密码——这是一个包含40项的密码数组,并对每个文件随机模拟,不知道正确的密码可能在数组中的什么位置。如果您知道密码,并且它是数组中的第一项,则需要约13秒。修订后的代码已发布。Excel创建现在已超出循环范围。我不再在循环中调用
…Quit()
,但发现关闭进程使用的内存更少。(40本工作簿=超过1 GB)。我喜欢你的解决方案,但无法将各个部分装配到合适的位置(一项工作中有两个回路是一个障碍)。我也找不到一种方法在成功后停止pwrd循环。我认为一个简单的
If
可以工作,但是不行。你能推荐一种方法来设置{break}标志或跟踪成功的PWRD吗?我试图将它们从pwrd阵列中删除,但没有成功。这是我使用PoSh的第三天,因此非常感谢任何进一步的建议!在这个if中:[switch]$DataOnly
[switch]$DataOnly,
[String]$Password
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
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
Get-RSJob | Select-Object -Property Name,Output