如何使用 Powershell 将哈希表与另一个哈希表进行比较?
How can I compare a hashtable with another one using Powershell?
我刚开始使用 powershell,但我现在的知识非常匮乏。
我有这个 .log 文件,如下所示:
18.7.2017 12:59:15 Starting thread: KEYWORD1
18.7.2017 12:59:33 Thread finished; ... KEYWORD1
18.7.2017 13:32:19 Starting thread: KEYWORD2
18.7.2017 13:34:8 Thread finished;... KEYWORD2
我现在想知道,启动的每个线程是否也已完成。
如果有未完成的线程我想将时间戳与当前时间进行比较。
我认为哈希表可以解决问题,这就是我想出的:
foreach($line in Get-Content $sourceDirectory)
{
if($line -like "*Starting thread*")
{
$arrStart = $line -split ' '
$startThreads=$arrStart[$arrStart.Length-1]
$hashmap1 = @{$arrEnd[$arrEnd.Length-1] = $arrEnd[1]}
}
if($line -like "*Thread finished*")
{
$arrEnd = $line -split ' '
$hashmap2 = @{$arrEnd[$arrEnd.Length-1] = $arrEnd[1]}
$endThreads=($arrEnd[1]+" "+$arrEnd[$arrEnd.Length-1])
}
}
现在如何比较这两个哈希图?
您似乎正在尝试制作两个哈希表,一个用于开始,一个用于完成。重要信息是关键字。与其制作散列表,因为你真的只需要一条信息,数组会是更好的数据类型。
# Find Lines with `Starting thread` and drop everything before the final space to get the array of KEYWORDS that started
$Start = (Select-String $sourceDirectory 'Starting thread') -replace '^.*Starting thread.*\s+'
# Find Lines with `Thread finished` and drop everything before the final space to get the array of KEYWORDS that finished
$Finish = (Select-String $sourceDirectory 'Thread finished') -replace '^.*Thread finished.*\s+'
# Find everything that started but hasn't finished.
$Start.where({$_ -notin $Finish})
注意:where
方法和 -notin
需要 PS4+。还假设线程不会多次启动和停止。
一种方法是使用 RegEx 将每一行分开,然后从细节创建一个新对象。例如:
Get-Content .\data.txt |
ForEach-Object {
if ($_ -match "^(?<time>(\d+\.){2}\d+ (\d{2}:){2}\d{2}).*(?<state>Starting|finished).*\b(?<keyword>\w+)$")
{
[PsCustomObject]@{
Keyword = $matches.keyword
Action = $(if($matches.state -eq "Starting"){"Start"}else{"Finish"})
Time = (Get-Date $matches.time)
}
}
}
假设您有一个包含以下内容的日志文件 (data.txt
):
18.7.2017 12:59:15 Starting thread: KEYWORD1
18.7.2017 13:32:19 Starting thread: KEYWORD2
18.7.2017 12:59:15 Starting thread: KEYWORD3
18.7.2017 13:34:18 Thread finished;... KEYWORD2
18.7.2017 12:59:15 Starting thread: KEYWORD4
18.7.2017 13:34:18 Thread finished;... KEYWORD3
18.7.2017 12:59:15 Starting thread: KEYWORD5
18.7.2017 13:34:18 Thread finished;... KEYWORD5
运行 上面的代码针对它,给出输出:
Keyword Action Time
------- ------ ----
KEYWORD1 Start 18/07/2017 12:59:15
KEYWORD2 Start 18/07/2017 13:32:19
KEYWORD3 Start 18/07/2017 12:59:15
KEYWORD2 Finish 18/07/2017 13:34:18
KEYWORD4 Start 18/07/2017 12:59:15
KEYWORD3 Finish 18/07/2017 13:34:18
KEYWORD5 Start 18/07/2017 12:59:15
KEYWORD5 Finish 18/07/2017 13:34:18
这与原始文件相比没有太大改进,但现在您有了一些对象,可以更轻松地处理它们。例如,您可以通过在最后一个括号后附加以下内容来查看哪些没有匹配 start/finish:
| Group-Object Keyword -NoElement | Sort-Object Count -Descending
这给出了这样的输出:
Count Name
----- ----
2 KEYWORD2
2 KEYWORD3
2 KEYWORD5
1 KEYWORD1
1 KEYWORD4
现在可以更轻松地查看哪些有 start/finish 对(例如,每组有 2 个项目)
这对于您的场景来说可能有点矫枉过正,但正如您所说的您是 PowerShell 的新手,我想我会提到它,因为将文本转换为这样的对象以进行处理通常非常有用。
JPBlanc recommends grouping the records in a comment on the question, and the Group-Object
cmdlet 确实提供了一个概念上优雅的解决方案:
注意:假设如果给定的关键字只有 一个 条目,则它始终是 starting 条目。
Select-String 'Starting thread:|Thread finished;' file.log |
Group-Object { (-split $_)[-1] } | Where-Object { $_.Count % 2 -eq 1 }
Select-String
调用只提取感兴趣的行(一个线程开始,一个线程结束),使用正则表达式(正则表达式)
Group-Object
调用在每行 ($_
),即关键字。
Where-Object
然后 returns 只有那些条目数为奇数的结果,即那些没有 配对的条目 , 表示已开始但未完成的线程。
这会产生如下内容:
Count Name Group
----- ---- -----
1 KEYWORD3 {/Users/jdoe/file.log:5:28.8.2018 08:59:16 Starting thread: KEYWORD3}
这可能不是您想要的格式,但考虑到输出是 objects,这在 PowerShell 中很典型,您可以根据自己的喜好以编程方式轻松处理它们。
从技术上讲,上述命令输出 [Microsoft.PowerShell.Commands.GroupInfo]
instances whose .Group
property in this case contains [Microsoft.PowerShell.Commands.MatchInfo]
个实例,如 Select-String
的输出。
以下代码扩展了上面的代码以生成自定义输出,报告自每个未完成的线程启动以来已经过去了多少时间:
$now = Get-Date
Select-String 'Starting thread:|Thread finished;' file.log |
Group-Object { (-split $_)[-1] } | Where-Object { $_.Count % 2 -eq 1 } | ForEach-Object {
foreach ($matchInfo in $_.Group) { # loop over started-only lines
$tokens = -split $matchInfo.Line # split into tokens by whitespace
$date, $time = $tokens[0..1] # extract date and time (first 2 tokens)
$keyword = $tokens[-1] # extract keyword (last token)
# Parse date+time into a [datetime] instance.
# Note: Depending on the current culture, [datetime]::Parse("$date $time") may do.
$start = [datetime]::ParseExact("$date $time", 'd\.M\.yyyy HH:mm:ss', [cultureinfo]::InvariantCulture)
# Custom output string containing how long ago the thread was started:
"Thread $keyword hasn't finished yet; time elapsed since it started: " +
($now - $start).ToString('g')
}
}
这会产生如下内容:
Thread KEYWORD3 hasn't finished yet; time elapsed since it started: 2:03:35.347563
2:03:35.347563
(2 小时,3 分钟,...)是 [TimeSpan]
instance that is the result of subtracting two points in time ([datetime]
个实例的字符串表示形式。
我刚开始使用 powershell,但我现在的知识非常匮乏。 我有这个 .log 文件,如下所示:
18.7.2017 12:59:15 Starting thread: KEYWORD1
18.7.2017 12:59:33 Thread finished; ... KEYWORD1
18.7.2017 13:32:19 Starting thread: KEYWORD2
18.7.2017 13:34:8 Thread finished;... KEYWORD2
我现在想知道,启动的每个线程是否也已完成。 如果有未完成的线程我想将时间戳与当前时间进行比较。
我认为哈希表可以解决问题,这就是我想出的:
foreach($line in Get-Content $sourceDirectory)
{
if($line -like "*Starting thread*")
{
$arrStart = $line -split ' '
$startThreads=$arrStart[$arrStart.Length-1]
$hashmap1 = @{$arrEnd[$arrEnd.Length-1] = $arrEnd[1]}
}
if($line -like "*Thread finished*")
{
$arrEnd = $line -split ' '
$hashmap2 = @{$arrEnd[$arrEnd.Length-1] = $arrEnd[1]}
$endThreads=($arrEnd[1]+" "+$arrEnd[$arrEnd.Length-1])
}
}
现在如何比较这两个哈希图?
您似乎正在尝试制作两个哈希表,一个用于开始,一个用于完成。重要信息是关键字。与其制作散列表,因为你真的只需要一条信息,数组会是更好的数据类型。
# Find Lines with `Starting thread` and drop everything before the final space to get the array of KEYWORDS that started
$Start = (Select-String $sourceDirectory 'Starting thread') -replace '^.*Starting thread.*\s+'
# Find Lines with `Thread finished` and drop everything before the final space to get the array of KEYWORDS that finished
$Finish = (Select-String $sourceDirectory 'Thread finished') -replace '^.*Thread finished.*\s+'
# Find everything that started but hasn't finished.
$Start.where({$_ -notin $Finish})
注意:where
方法和 -notin
需要 PS4+。还假设线程不会多次启动和停止。
一种方法是使用 RegEx 将每一行分开,然后从细节创建一个新对象。例如:
Get-Content .\data.txt |
ForEach-Object {
if ($_ -match "^(?<time>(\d+\.){2}\d+ (\d{2}:){2}\d{2}).*(?<state>Starting|finished).*\b(?<keyword>\w+)$")
{
[PsCustomObject]@{
Keyword = $matches.keyword
Action = $(if($matches.state -eq "Starting"){"Start"}else{"Finish"})
Time = (Get-Date $matches.time)
}
}
}
假设您有一个包含以下内容的日志文件 (data.txt
):
18.7.2017 12:59:15 Starting thread: KEYWORD1
18.7.2017 13:32:19 Starting thread: KEYWORD2
18.7.2017 12:59:15 Starting thread: KEYWORD3
18.7.2017 13:34:18 Thread finished;... KEYWORD2
18.7.2017 12:59:15 Starting thread: KEYWORD4
18.7.2017 13:34:18 Thread finished;... KEYWORD3
18.7.2017 12:59:15 Starting thread: KEYWORD5
18.7.2017 13:34:18 Thread finished;... KEYWORD5
运行 上面的代码针对它,给出输出:
Keyword Action Time
------- ------ ----
KEYWORD1 Start 18/07/2017 12:59:15
KEYWORD2 Start 18/07/2017 13:32:19
KEYWORD3 Start 18/07/2017 12:59:15
KEYWORD2 Finish 18/07/2017 13:34:18
KEYWORD4 Start 18/07/2017 12:59:15
KEYWORD3 Finish 18/07/2017 13:34:18
KEYWORD5 Start 18/07/2017 12:59:15
KEYWORD5 Finish 18/07/2017 13:34:18
这与原始文件相比没有太大改进,但现在您有了一些对象,可以更轻松地处理它们。例如,您可以通过在最后一个括号后附加以下内容来查看哪些没有匹配 start/finish:
| Group-Object Keyword -NoElement | Sort-Object Count -Descending
这给出了这样的输出:
Count Name
----- ----
2 KEYWORD2
2 KEYWORD3
2 KEYWORD5
1 KEYWORD1
1 KEYWORD4
现在可以更轻松地查看哪些有 start/finish 对(例如,每组有 2 个项目)
这对于您的场景来说可能有点矫枉过正,但正如您所说的您是 PowerShell 的新手,我想我会提到它,因为将文本转换为这样的对象以进行处理通常非常有用。
JPBlanc recommends grouping the records in a comment on the question, and the Group-Object
cmdlet 确实提供了一个概念上优雅的解决方案:
注意:假设如果给定的关键字只有 一个 条目,则它始终是 starting 条目。
Select-String 'Starting thread:|Thread finished;' file.log |
Group-Object { (-split $_)[-1] } | Where-Object { $_.Count % 2 -eq 1 }
Select-String
调用只提取感兴趣的行(一个线程开始,一个线程结束),使用正则表达式(正则表达式)Group-Object
调用在每行 ($_
),即关键字。Where-Object
然后 returns 只有那些条目数为奇数的结果,即那些没有 配对的条目 , 表示已开始但未完成的线程。
这会产生如下内容:
Count Name Group
----- ---- -----
1 KEYWORD3 {/Users/jdoe/file.log:5:28.8.2018 08:59:16 Starting thread: KEYWORD3}
这可能不是您想要的格式,但考虑到输出是 objects,这在 PowerShell 中很典型,您可以根据自己的喜好以编程方式轻松处理它们。
从技术上讲,上述命令输出 [Microsoft.PowerShell.Commands.GroupInfo]
instances whose .Group
property in this case contains [Microsoft.PowerShell.Commands.MatchInfo]
个实例,如 Select-String
的输出。
以下代码扩展了上面的代码以生成自定义输出,报告自每个未完成的线程启动以来已经过去了多少时间:
$now = Get-Date
Select-String 'Starting thread:|Thread finished;' file.log |
Group-Object { (-split $_)[-1] } | Where-Object { $_.Count % 2 -eq 1 } | ForEach-Object {
foreach ($matchInfo in $_.Group) { # loop over started-only lines
$tokens = -split $matchInfo.Line # split into tokens by whitespace
$date, $time = $tokens[0..1] # extract date and time (first 2 tokens)
$keyword = $tokens[-1] # extract keyword (last token)
# Parse date+time into a [datetime] instance.
# Note: Depending on the current culture, [datetime]::Parse("$date $time") may do.
$start = [datetime]::ParseExact("$date $time", 'd\.M\.yyyy HH:mm:ss', [cultureinfo]::InvariantCulture)
# Custom output string containing how long ago the thread was started:
"Thread $keyword hasn't finished yet; time elapsed since it started: " +
($now - $start).ToString('g')
}
}
这会产生如下内容:
Thread KEYWORD3 hasn't finished yet; time elapsed since it started: 2:03:35.347563
2:03:35.347563
(2 小时,3 分钟,...)是 [TimeSpan]
instance that is the result of subtracting two points in time ([datetime]
个实例的字符串表示形式。