将进度条添加到现有脚本

Adding a Progressbar to an existing Script

我有一个脚本来过滤 SMTP 日志文件,它分为两部分,第一部分

$result = Get-Content (path) | ForEach-Object {
    if($_ -match '(\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}).*\(((?:\d{1,3}\.){3}\d{1,3})\) disconnected\.?\s+(\d+) message\[s\]'){
        try {
            $dns = [System.Net.Dns]::GetHostEntry($matches[2]).HostName
        }
        catch { 
            $dns = 'Not available' 
        }
        [PsCustomObject]@{
            IP       = $matches[2]
            Messages = [int]$matches[3]
            DNSName  = $dns
            Date     = [datetime]::ParseExact($matches[1], 'dd.MM.yyyy HH:mm:ss', $null)
        }
    }
}

过滤我需要的所有信息,例如IP、传入消息编号、DnsName。第二个

$cumulative = $result | Group-Object -Property IP | ForEach-Object {
    [PsCustomObject]@{
        IP = $_.Name
        Messages = ($_.Group | Measure-Object -Property Messages -Sum).Sum
        DNSName = $_.Group[0].DNSName
        Date    = ($_.Group | Sort-Object Date)[-1].Date
    }
}

只计算每个 IP 的传入消息,并且只显示最近的日期。

现在我想添加一个进度条,因为我不确定是否可以将它们组合成一个命令我可能会尝试在两个命令中添加一个进度条。我阅读了有关 Write-progress 命令的文章,但我不知道如何将其包含到现有代码中。

您实际上可以将这两个命令结合起来。我们将在解析日志文件的行时使用 $cumulative 进行分组。 $cumulative 是一个 [Hashtable],因此我们可以通过 IP 汇总数据,并实时汇总消息数。这意味着操作更少,因此性能更高。

对于进度条,Write-Progress是一个合适的选择。要在这里使用它,我们必须快速估计日志文件的行数。 我们不应该在开始时读取日志文件内容来计算行数,因为它会增加一个耗时的繁重 I/O 操作,其唯一目的是显示进度条。为避免这种情况,我们将根据文件大小(以字节为单位)除以其行的估计长度平均值来假设其行数。获取文件大小并进行此数学运算非常快,因此我们可以为进度条负担得起。有了这个估计的行数,我们就可以在解析文件的同时以非常低的时间成本更新进度条。在这样做的同时,我们可以调整估计的行长度,以便对我们阅读的每一行进行更准确的进度计算。

这是相应的脚本,将两个操作与一个全局进度条结合起来:

$lfile="path"
$cumulative=@{};
# We need the file size for progress bar
$loginfo=Get-item -Path $lfile
if (!$loginfo) {
  Write-Host ("Can't find log file... : "+$lfile)
  exit 1;
}
# We'll keep a estimated line length average and the corresponding estimated number of lines of the file
# We could have started with a fixed average, but let's estimate it while rading the file
$estimateaveragelinelength=0 
$estimatelinenumber=1
$totallinelength=0 
# Counters to update the progress bar
$percent=0
$curline=0
Write-Progress -Activity ("Parsing log file "+$lfile) -Status "Start" -Id 1 -PercentComplete 0
$result = Get-Content ($lfile) | ForEach-Object {
    if($_ -match '(\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}).*\(((?:\d{1,3}\.){3}\d{1,3})\) disconnected\.?\s+(\d+) message\[s\]'){
        try {
            $dns = [System.Net.Dns]::GetHostEntry($matches[2]).HostName
        }
        catch { 
            $dns = 'Not available' 
        }
        # We store the object we'll add to $result.
        # If you have no more usage of $result after, you should remove $result.
        $msg=[PsCustomObject]@{
            IP       = $matches[2]
            Messages = [int]$matches[3]
            DNSName  = $dns
            Date     = [datetime]::ParseExact($matches[1], 'dd.MM.yyyy HH:mm:ss', $null)
        }
        # We do the grouping on the fly by IP, summing the number of messages
        # and keeping only the most recent date by IP
        if (!$cumulative.ContainsKey($msg.IP)) {
          $cumulative.Add($msg.IP, [PsCustomObject]@{
            IP = $msg.IP
            Messages = $msg.Messages
            DNSName = $dns
            Date    = $msg.Date
          })
        } else {
          $cumulative[$msg.IP].Messages+=$msg.Messages;
          if ($cumulative[$msg.IP].Date -lt $msg.Date) {
            $cumulative[$msg.IP].Date = $msg.Date;
          }
        }
        $msg
    }
    # Count number of lines currently read
    $curline++;
    # Adjust the estimated average line length from current line 
    # and then the corresponding estimated number of lines of the file
    if ($estimateaveragelinelength -eq 0) {
      # First read, we start with the first line length
      $estimateaveragelinelength = $_.Length
      $totallinelength = $_.Length
    } else {
      $totallinelength += $_.Length
      # Adjust the average length with the current line Length
      $estimateaveragelinelength = [math]::round($totallinelength / $curline)
    }
    # We'll ignore empty lines
    if ($estimateaveragelinelength -ne 0) {
      # Adjust the corresponding estimated number of lines of the file with the actual average line length
      # As $totallinelength  can't be greater than $loginfo.Length, we should never excess 100%, 
      # thus $estimatelinenumber can't be greater than the real number of  the lines
      $estimatelinenumber=[math]::round(($loginfo.Length / $estimateaveragelinelength))
      # Do the math and update progress bar
      $curpercent=[math]::round(($curline/$estimatelinenumber)*100);
      if ($curpercent -ne $percent) {
        $percent=$curpercent
        Write-Progress -Activity ("Parsing log file "+$lfile) -Status "In progress" -Id 1 -PercentComplete $curpercent
      }
    }
}
Write-Progress -Activity ("Parsing log file "+$lfile) -Status "Done" -Id 1 -PercentComplete 100 -Completed
$cumulative