如何使用Powershell将哈希表与另一个哈希表进行比较?

时间:2018-08-28 12:48:47

标签: powershell hashmap hashtable

我刚开始使用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])
    }
}

现在如何比较这两个哈希图?

3 个答案:

答案 0 :(得分:2)

JPBlanc建议在问题注释中对记录进行分组,而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调用通过每行([-1])上最后一个(-split ...)空格分隔的标记($_)对结果行进行分组,即,关键字。

  • Where-Object然后仅返回条目数量为奇数的结果,即那些不是 paired 的条目,它们表示已启动但未完成的线程

这将产生如下内容:

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]实例,其.Group属性在此情况下包含[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]实例的字符串表示形式,它是减去两个时间点([datetime]实例)的结果。

答案 1 :(得分: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 +。此外,还假设线程不会多次启动和停止。

答案 2 :(得分:1)

执行此操作的一种方法是使用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

与原始文件相比,这没有多大改进,但是现在有了一些对象,您可以更轻松地处理它们。例如,您可以通过在最后一个括号后添加以下内容来查看哪些没有匹配的开始/结束:

| Group-Object Keyword -NoElement | Sort-Object Count -Descending

这给出了这样的输出:

Count Name                     
----- ----                     
    2 KEYWORD2                 
    2 KEYWORD3                 
    2 KEYWORD5                 
    1 KEYWORD1                 
    1 KEYWORD4  

现在可以更容易地看到哪些有开始/结束对(例如,每组有2个项目)

这可能对您的情况有些矫kill过正,但是正如您所说的那样,PowerShell是我的新手,我想我提到它,因为将文本转换成这样的对象进行处理通常非常有用。