从 Powershell 中的数组中获取所有唯一子字符串
Get all unique substrings from array in Powershell
我从日志中收集了一组字符串,我试图将其解析为唯一的条目:
function Scan ($path, $logPaths, $pattern)
{
$logPaths | % `
{
$file = $_.FullName
Write-Host "`n[$file]"
Get-Content $file | Select-String -Pattern $pattern -CaseSensitive - AllMatches | % `
{
$regexDateTime = New-Object System.Text.RegularExpressions.Regex "((?:\d{4})-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}(,\d{3})?)"
$matchDate = $regexDateTime.match($_)
if($matchDate.success)
{
$loglinedate = [System.DateTime]::ParseExact($matchDate, "yyyy-MM-dd HH:mm:ss,FFF", [System.Globalization.CultureInfo]::InvariantCulture)
if ($loglinedate -gt $laterThan)
{
$date = $($_.toString().TrimStart() -split ']')[0]
$message = $($_.toString().TrimStart() -split ']')[1]
$messageArr += ,$date,$message
}
}
}
$messageArr | sort $message -Unique | foreach { Write-Host -f Green $date$message}
}
}
所以对于这个输入:
2015-09-04 07:50:06 [20] 警告 Core.Ports.Services.ReferenceDataCheckers.SharedCheckers.DocumentLibraryMustExistService - 找不到 DocumentLibrary 3。
2015-09-04 07:50:06 [20] 警告 Core.Ports.Services.ReferenceDataCheckers.SharedCheckers.DocumentLibraryMustExistService - 找不到 DocumentLibrary 3。
2015-09-04 07:50:16 [20] WARN Brighter - 消息 abc123 已被消费者标记为已过时,因为该实体在消费者端具有更高版本。
只应返回后两个条目
我在过滤掉 $message 的重复项时遇到问题:目前正在返回所有条目(sort -Unique 的行为与我预期的不同)。我还需要根据过滤后的 $message 返回正确的 $date。
我很难解决这个问题,有人可以帮忙吗?
我们可以做你想做的,但首先让我们稍微备份一下,以帮助我们做得更好。现在你有一个数组数组,一般来说很难处理。如果您有一个对象数组,并且这些对象具有 Date 和 Message 等属性,那就更好了。让我们从这里开始吧。
if ($loglinedate -gt $laterThan)
{
$date = $($_.toString().TrimStart() -split ']')[0]
$message = $($_.toString().TrimStart() -split ']')[1]
$messageArr += ,$date,$message
}
将成为...
if ($loglinedate -gt $laterThan)
{
[Array]$messageArr += [PSCustomObject]@{
'date' = $($_.toString().TrimStart() -split ']')[0]
'message' = $($_.toString().TrimStart() -split ']')[1]
}
}
这会产生一个对象数组,每个对象都有两个属性,Date 和 Message。这将更容易使用。
如果您只想要使用 Group-Object
命令轻松完成的任何消息的最新版本:
$FilteredArr = $messageArr | Group Message | ForEach{$_.Group|sort Date|Select -Last 1}
那么如果你想把它显示在屏幕上,你可以这样做:
$Filtered|ForEach{Write-Host -f Green ("{0}`t{1}" -f $_.Date, $_.Message)}
我的看法(未测试):
function Scan ($path, $logPaths, $pattern)
{
$regex = '(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s(.+)'
$ht = @{}
$logPaths | % `
{
$file = $_.FullName
Write-Host "`n[$file]"
Get-Content $file | Select-String -Pattern $pattern -CaseSensitive -AllMatches | % `
{
if ($_.line -match $regex -and $ht[$matches[2]] -gt $matches[1])
{ $ht[$matches[2]] = $matches[1] }
}
$ht.GetEnumerator() |
sort Value |
foreach { Write-Host -f Green "$($_.Value)$($_.Name)" }
}
}
这会在时间戳处拆分文件,并将各部分加载到散列中 table,使用错误消息作为键,将时间戳作为数据(这将对流中的消息进行重复数据删除) ).
时间戳已经是 string-sortable 格式 (yyyy-MM-dd HH:mm:ss),所以真的没有必要将它们转换为 [datetime] 来找到最新的。只需进行直接字符串比较,如果传入时间戳大于该消息的现有值,则将现有值替换为新值。
完成后,您应该有一个散列 table,其中包含找到的每条唯一消息的键,其值是为该消息找到的最新时间戳。
我从日志中收集了一组字符串,我试图将其解析为唯一的条目:
function Scan ($path, $logPaths, $pattern)
{
$logPaths | % `
{
$file = $_.FullName
Write-Host "`n[$file]"
Get-Content $file | Select-String -Pattern $pattern -CaseSensitive - AllMatches | % `
{
$regexDateTime = New-Object System.Text.RegularExpressions.Regex "((?:\d{4})-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}(,\d{3})?)"
$matchDate = $regexDateTime.match($_)
if($matchDate.success)
{
$loglinedate = [System.DateTime]::ParseExact($matchDate, "yyyy-MM-dd HH:mm:ss,FFF", [System.Globalization.CultureInfo]::InvariantCulture)
if ($loglinedate -gt $laterThan)
{
$date = $($_.toString().TrimStart() -split ']')[0]
$message = $($_.toString().TrimStart() -split ']')[1]
$messageArr += ,$date,$message
}
}
}
$messageArr | sort $message -Unique | foreach { Write-Host -f Green $date$message}
}
}
所以对于这个输入:
2015-09-04 07:50:06 [20] 警告 Core.Ports.Services.ReferenceDataCheckers.SharedCheckers.DocumentLibraryMustExistService - 找不到 DocumentLibrary 3。
2015-09-04 07:50:06 [20] 警告 Core.Ports.Services.ReferenceDataCheckers.SharedCheckers.DocumentLibraryMustExistService - 找不到 DocumentLibrary 3。
2015-09-04 07:50:16 [20] WARN Brighter - 消息 abc123 已被消费者标记为已过时,因为该实体在消费者端具有更高版本。
只应返回后两个条目
我在过滤掉 $message 的重复项时遇到问题:目前正在返回所有条目(sort -Unique 的行为与我预期的不同)。我还需要根据过滤后的 $message 返回正确的 $date。
我很难解决这个问题,有人可以帮忙吗?
我们可以做你想做的,但首先让我们稍微备份一下,以帮助我们做得更好。现在你有一个数组数组,一般来说很难处理。如果您有一个对象数组,并且这些对象具有 Date 和 Message 等属性,那就更好了。让我们从这里开始吧。
if ($loglinedate -gt $laterThan)
{
$date = $($_.toString().TrimStart() -split ']')[0]
$message = $($_.toString().TrimStart() -split ']')[1]
$messageArr += ,$date,$message
}
将成为...
if ($loglinedate -gt $laterThan)
{
[Array]$messageArr += [PSCustomObject]@{
'date' = $($_.toString().TrimStart() -split ']')[0]
'message' = $($_.toString().TrimStart() -split ']')[1]
}
}
这会产生一个对象数组,每个对象都有两个属性,Date 和 Message。这将更容易使用。
如果您只想要使用 Group-Object
命令轻松完成的任何消息的最新版本:
$FilteredArr = $messageArr | Group Message | ForEach{$_.Group|sort Date|Select -Last 1}
那么如果你想把它显示在屏幕上,你可以这样做:
$Filtered|ForEach{Write-Host -f Green ("{0}`t{1}" -f $_.Date, $_.Message)}
我的看法(未测试):
function Scan ($path, $logPaths, $pattern)
{
$regex = '(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s(.+)'
$ht = @{}
$logPaths | % `
{
$file = $_.FullName
Write-Host "`n[$file]"
Get-Content $file | Select-String -Pattern $pattern -CaseSensitive -AllMatches | % `
{
if ($_.line -match $regex -and $ht[$matches[2]] -gt $matches[1])
{ $ht[$matches[2]] = $matches[1] }
}
$ht.GetEnumerator() |
sort Value |
foreach { Write-Host -f Green "$($_.Value)$($_.Name)" }
}
}
这会在时间戳处拆分文件,并将各部分加载到散列中 table,使用错误消息作为键,将时间戳作为数据(这将对流中的消息进行重复数据删除) ).
时间戳已经是 string-sortable 格式 (yyyy-MM-dd HH:mm:ss),所以真的没有必要将它们转换为 [datetime] 来找到最新的。只需进行直接字符串比较,如果传入时间戳大于该消息的现有值,则将现有值替换为新值。
完成后,您应该有一个散列 table,其中包含找到的每条唯一消息的键,其值是为该消息找到的最新时间戳。