在PowerShell中解析数千个小文件的最快方法

在PowerShell中解析数千个小文件的最快方法,powershell,Powershell,我在网络共享上有16000多个库存日志文件,大小从3-5 KB不等。 示例文件如下所示: ## System Info SystemManufacturer:=:Dell Inc. SystemModel:=:OptiPlex GX620 SystemType:=:X86-based PC ChassisType:=:6 (Mini Tower) ## System Type isLaptop=No 我需要把它们放到数据库中,所以

我在网络共享上有16000多个库存日志文件,大小从3-5 KB不等。 示例文件如下所示:

## System Info
SystemManufacturer:=:Dell Inc.                
SystemModel:=:OptiPlex GX620               
SystemType:=:X86-based PC
ChassisType:=:6 (Mini Tower)

## System Type
isLaptop=No
我需要把它们放到数据库中,所以我开始解析它们,并为它们创建一个自定义对象,以后我可以用它来检查重复项、规范化等等

使用如下所示的代码片段进行初始解析大约需要7.5分钟

Foreach ($invlog in $invlogs) {
    $content = gc $invlog.FullName -ReadCount 0
    foreach ($line in $content) {
        if ($line -match '^#|^\s*$') { continue }
        $invitem,$value=$line -split ':=:'
        [PSCustomObject]@{Name=$invitem;Value=$value}
    }
}
我开始对它进行优化,经过几次尝试和错误处理,最终得到了这个需要2分钟4秒的结果:

 Foreach ($invlog in $invlogs) {
        foreach ($line in ([System.IO.File]::ReadLines("$($invlog.FullName)") -match '^\w')  ) {
           $invitem,$value=$line -split ':=:'
           [PSCustomObject]@{name=$invitem;Value=$value}  #2.04mins
        }
    }
我也尝试过用散列代替PSCustomObject,但令我惊讶的是,它花费了更长的5分26秒

       Foreach ($invlog in $invlogs) {                        
        $hash=@{}        
        foreach ($line in ([System.IO.File]::ReadLines("$($invlog.FullName)") -match $propertyline)  ) {

           $invitem,$value=$line -split ':=:'
           $hash[$invitem]=$value #5.26mins
        }
    }

这里最快的方法是什么

看看这是否更快:

Foreach ($invlog in $invlogs) {
@(gc $invlog.FullName -ReadCount 0) -notmatch '^#|^\s*$' |
 foreach {
          $invitem,$value=$line -split ':=:'
          [PSCustomObject]@{Name=$invitem;Value=$value}
         }
}
当-match和-notmatch运算符应用于数组时,将返回满足匹配条件的所有元素,因此您无需为要排除的行测试每一行

您真的想为每一行创建一个PS对象,还是只为每个文件创建一个PS对象

如果希望每个文件有一个对象,请查看这是否更快: 多行正则表达式消除了行数组,并使用过滤器代替foreach来创建散列项

 $regex = [regex]'(?ms)^(\w+):=:([^\r]+)'
 filter make-hash { @{$_.groups[1].value = $_.groups[2].value} }

Foreach ($invlog in $invlogs) {
$regex.matches([io.file]::ReadAllText($invlog.fullname)) | make-hash
 }

切换到使用多行正则表达式和[io.file]::ReadAllText]的目的是简化Powershell在内部对文件输入所做的操作。[io.file]::ReadAllText的结果将是一个字符串对象,这是一种比[io.file]::ReadAllLines将生成的字符串数组简单得多的对象类型,并且需要更少的内部construct开销。过滤器本质上只是一个函数的进程块——它将对从管道中到达它的每个对象运行一次,因此它模拟foreach对象的操作,但实际上运行得稍快一些。我不太了解内部结构,无法确切地告诉您原因。这两种变化都需要更多的编码,只会导致性能的轻微提高。在我的测试中,切换到多行每个文件获得了大约.1ms,从foreach对象切换到filter又获得了.1ms。您可能不会经常看到这些技术的使用,因为与所需的额外编码工作相比,回报率较低,但是当你开始用160K次迭代乘以ms的分数时,它就变得非常重要了。

看看这是否更快:

Foreach ($invlog in $invlogs) {
@(gc $invlog.FullName -ReadCount 0) -notmatch '^#|^\s*$' |
 foreach {
          $invitem,$value=$line -split ':=:'
          [PSCustomObject]@{Name=$invitem;Value=$value}
         }
}
当-match和-notmatch运算符应用于数组时,将返回满足匹配条件的所有元素,因此您无需为要排除的行测试每一行

您真的想为每一行创建一个PS对象,还是只为每个文件创建一个PS对象

如果希望每个文件有一个对象,请查看这是否更快: 多行正则表达式消除了行数组,并使用过滤器代替foreach来创建散列项

 $regex = [regex]'(?ms)^(\w+):=:([^\r]+)'
 filter make-hash { @{$_.groups[1].value = $_.groups[2].value} }

Foreach ($invlog in $invlogs) {
$regex.matches([io.file]::ReadAllText($invlog.fullname)) | make-hash
 }

切换到使用多行正则表达式和[io.file]::ReadAllText]的目的是简化Powershell在内部对文件输入所做的操作。[io.file]::ReadAllText的结果将是一个字符串对象,这是一种比[io.file]::ReadAllLines将生成的字符串数组简单得多的对象类型,并且需要更少的内部construct开销。过滤器本质上只是一个函数的进程块——它将对从管道中到达它的每个对象运行一次,因此它模拟foreach对象的操作,但实际上运行得稍快一些。我不太了解内部结构,无法确切地告诉您原因。这两种变化都需要更多的编码,只会导致性能的轻微提高。在我的测试中,切换到多行每个文件获得了大约.1ms,从foreach对象切换到filter又获得了.1ms。您可能不会经常看到这些技术的使用,因为与所需的额外编码工作相比,回报率较低,但是,当您开始将ms的这些分数乘以160K次迭代时,它变得非常重要。

您不希望每个系统都有一个对象,而不是每个键值对吗s 这样地。。通过将Get内容替换为.Net方法,您可能会节省一些时间

Get-ChildItem -Filter *.txt -Path <path to files> | ForEach-Object {

    $ht = @{}

    Get-Content $_ | Where-Object { $_ -match ':=:' } | ForEach-Object {

        $ht[($_ -split ':=:')[0].Trim()] = ($_ -split ':=:')[1].Trim()

    }

    [pscustomobject]$ht

}

ChassisType                          SystemManufacturer                   SystemType                          SystemModel
-----------                          ------------------                   ----------                          -----------
6 (Mini Tower)                       Dell Inc.                            X86-based PC                        OptiPlex GX620

您不希望每个系统都有一个对象,而不是每个键值对吗s 这样地。。通过将Get内容替换为.Net方法,您可能会节省一些时间

Get-ChildItem -Filter *.txt -Path <path to files> | ForEach-Object {

    $ht = @{}

    Get-Content $_ | Where-Object { $_ -match ':=:' } | ForEach-Object {

        $ht[($_ -split ':=:')[0].Trim()] = ($_ -split ':=:')[1].Trim()

    }

    [pscustomobject]$ht

}

ChassisType                          SystemManufacturer                   SystemType                          SystemModel
-----------                          ------------------                   ----------                          -----------
6 (Mini Tower)                       Dell Inc.                            X86-based PC                        OptiPlex GX620
试试这个:

Foreach ($invlog in $invlogs) {
    $output = @{}
    foreach ($line in ([IO.File]::ReadLines("$($invlog.FullName)") -ne '')  ) {
        if ($line.Contains(":=:")) {
            $item, $value = $line.Split(":=:") -ne '' 
            $output[$item] = $value
        }        

    }

    New-Object PSObject -Property $output
}
一般来说,正则表达式有时很酷,但总是比较慢。

试试这个:

Foreach ($invlog in $invlogs) {
    $output = @{}
    foreach ($line in ([IO.File]::ReadLines("$($invlog.FullName)") -ne '')  ) {
        if ($line.Contains(":=:")) {
            $item, $value = $line.Split(":=:") -ne '' 
            $output[$item] = $value
        }        

    }

    New-Object PSObject -Property $output
}

一般来说,正则表达式有时很酷,但总是比较慢。

3分钟9秒。我最初用这种方式编写正则表达式,但后来改为“\w”进行测试,这样做速度更快。不确定原因。这是2分钟30秒:[System.IO.File]中的foreach$行::ReadLines$$invlog.FullName-notmatch'^ |^\s*$'{$invitem,$value=$line-split':==:';[PSCustomObject]@{name=$invitem;value=$value}}您在该共享所在的服务器上启用了远程处理吗?看起来不是这样。服务器人员尚未发现powershell。完成。如果你想讨论,需要把这个带到聊天室

再过1.3分钟和9秒。我最初用这种方式编写正则表达式,但后来改为“\w”进行测试,这样做速度更快。不确定原因。这是2分钟30秒:[System.IO.File]中的foreach$行::ReadLines$$invlog.FullName-notmatch'^ |^\s*$'{$invitem,$value=$line-split':==:';[PSCustomObject]@{name=$invitem;value=$value}}您在该共享所在的服务器上启用了远程处理吗?看起来不是这样。服务器人员尚未发现powershell。完成。如果您想进一步讨论,需要将此内容带到聊天室。这与我的哈希尝试类似,但您有额外的管道,这会使它慢得多:8分钟18秒。此外,是的,您对我的意图是正确的,并将哈希分配回CustomObject,但我知道如何为每台计算机进行分区。我感兴趣的部分是提高读取和处理文件的性能。你说得对。这个想法是为了节省时间,因为创建对象需要一些时间,所以将对象创建延迟到机器完全扫描为止。但是说实话。如果性能对您如此重要,您应该编写一个控制台应用程序,而不是使用powershell。powershell有很多开销。这与我的哈希尝试类似,但你有额外的管道,这使它慢得多:8分钟18秒。此外,是的,你对我的意图是正确的,并将哈希分配回CustomObject,但我知道如何为每台计算机对其进行分区。我感兴趣的部分是提高读取和处理文件的性能。你说得对。这个想法是为了节省时间,因为创建对象需要一些时间,所以将对象创建延迟到机器完全扫描为止。但是说实话。如果性能对您如此重要,您应该编写一个控制台应用程序,而不是使用powershell。powershell有很多开销。感谢您的输入,很高兴知道!不过我有6分5秒的时间。您认为它可以进一步改进吗?实际上,我有一个脚本,我最近为类似的目的而构建,它使用文件流。如果只需要前几行,则速度会快得多,因为这样可以避免读取整个文件。请联系获取脚本。感谢您的输入,很高兴知道!不过我有6分5秒的时间。您认为它可以进一步改进吗?实际上,我有一个脚本,我最近为类似的目的而构建,它使用文件流。如果只需要前几行,则速度会快得多,因为这样可以避免读取整个文件。请与我联系以获取脚本。