我在网络共享上有超过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
}
}
在这里使用最快的方法是什么?
答案 0 :(得分:3)
看看这是否更快:
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()将生成的字符串数组更简单的对象类型,并且需要更少的开销在内部进行。过滤器本质上只是一个函数的Process块 - 它将为从管道到达它的每个对象运行一次,因此它模拟foreach-object的动作,但实际运行速度稍快(我不知道内部足以告诉你究竟为什么)。这两种变化都需要更多编码,只会导致性能略有提高。在我的测试中,切换到多行每个文件大约增加.1ms,并从foreach-object更改为过滤器另一个.1 ms。您可能不会经常使用这些技术,因为与所需的额外编码工作相比回报较低,但是当您开始将这些ms的分数乘以160K迭代时,它会变得非常重要。
答案 1 :(得分:1)
试试这个:
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
}
作为一般规则,正则表达式有时很酷但总是较慢。
答案 2 :(得分:0)
您不希望每个系统都有一个对象,而不是每个键值对吗? :S
像这样..通过将Get-Content
替换为.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