提高我的PowerShell脚本的效率

时间:2018-11-01 23:00:18

标签: performance powershell coding-efficiency

下面的代码从 list.txt 文件中搜索400多个数字,以查看该文件是否存在于指定文件夹路径内的任何文件中。

该脚本非常慢,并且尚未完成,因为它在运行25分钟后仍未完成。我们正在搜索的文件夹为 507 MB (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 :(得分:6)

以下内容将大大加快您的工作速度:

如果意图确实是在文件名中查找搜索词:

$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

提高性能的关键:

  • 使用单个命令执行搜索,方法是将 all 个搜索词传递给Select-String

  • 与其使用New-ObjectAdd-Member在脚本块中构造自定义对象,不如让Select-Object使用calculated properties在管道中直接为您构造对象

  • 与其使用+=迭代构建中间数组-每次在后台重新创建-而是使用单个管道将结果对象直接传递到{{ 1}}。

答案 1 :(得分:1)

因此,您发布的PowerShell代码中肯定有一些可以改进的基本内容,但可能仍然不是很快。根据您提供给我们的样本,我假设您正在寻找将文件名与单词列表进行匹配的方法。您正在遍历单词列表(400次迭代),并且在每个循环中都遍历所有1,119个文件。总共有447,600次迭代!

假设您无法减少循环中的迭代次数,那么让我们加快每次迭代的速度就可以了。 Add-Member cmdlet真的很慢,因此通过将哈希表强制转换为[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
}

尝试两者并测试性能。

答案 2 :(得分:0)

因此,如果加快循环速度不能满足您的需要,请尝试完全删除循环。您可以使用正则表达式并将所有单词连接在一起:

$Words = Get-Content -Path $WordList
$Files = Get-ChildItem -Path $Path -Recurse -File
$WordRegex = $Words -join '|'
$Files | Where basename -match $WordRegex