【问题标题】:Read numbers from multiple files and sum从多个文件中读取数字并求和
【发布时间】:2017-04-11 21:21:31
【问题描述】:

我有一个日志文件 C:\temp\data.log 它包含以下数据:

totalSize = 222,6GB

totalSize = 4,2GB

totalSize = 56,2GB

我的目标是从文件中提取数字并将它们总结起来,包括逗号后的数字。到目前为止,如果我不正则表达式包含在逗号后的值中的数字,并且只使用逗号前面的数字,它就可以工作。我遇到的另一个问题是,如果文件只包含一行,如下例所示,如果它只包含一行,它会将数字 222 拆分为三个文件,其中三个文件中包含数字 2。如果上面的日志文件包含 2 行或更多行,则它可以正常工作并按应有的方式总结,只要我不使用带逗号的值。

totalSize                      = 222,6GB

以下是正则表达式添加到现有变量 $regex 末尾的一些代码,其中包括:

[,](\d{1,})

我没有包含上面的正则表达式,因为它没有正确总结。

整个脚本如下:

#Create path variable to store contents grabbed from $log_file
$extracted_strings = "C:\temp\amount.txt"
#Create path variable to read from original file
$log_file = "C:\temp\data.log"
#Read data from file $log_file
Get-Content -Path $log_file | Select-String "(totalSize = )" | out-file $extracted_strings
#Create path variable to write only numbers to file $output_numbers
$output_numbers = "C:\temp\amountresult.log"
#Create path variable to write to file jobblog1
$joblog1_file = "C:\temp\joblog1.txt"
#Create path variable to write to file jobblog2
$joblog2_file = "C:\temp\joblog2.txt"
#Create path variable to write to file jobblog3
$joblog3_file = "C:\temp\joblog3.txt"
#Create path variable to write to file jobblog4
$joblog4_file = "C:\temp\joblog4.txt"
#Create path variable to write to file jobblog5
$joblog5_file = "C:\temp\joblog5.txt"
#Create pattern variable to read with select string
$regex = "[= ](\d{1,})"
select-string -Path $extracted_strings -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }  > $output_numbers
(Get-Content -Path $output_numbers)[0..0] -replace '\s' > $joblog1_file
(Get-Content -Path $output_numbers)[1..1] -replace '\s' > $joblog2_file
(Get-Content -Path $output_numbers)[2..2] -replace '\s' > $joblog3_file
(Get-Content -Path $output_numbers)[3..3] -replace '\s' > $joblog4_file
(Get-Content -Path $output_numbers)[4..4] -replace '\s' > $joblog5_file
$jobdata0 = (Get-Content -Path $joblog1_file)
$jobdata1 = (Get-Content -Path $joblog2_file)
$jobdata2 = (Get-Content -Path $joblog3_file)
$jobdata3 = (Get-Content -Path $joblog4_file)
$jobdata4 = (Get-Content -Path $joblog5_file)
$result = $jobdata0  + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
$result

所以我的问题是:

  1. 如果文件 C:\temp\data.log 仅包含一个字符串而不将该单个数字分成多个文件,我如何才能使其工作。如果它包含多个字符串,它也应该可以工作,因为它现在可以处理多个字符串。

  2. 如何在计算中包含逗号值?

如果我运行这个脚本,我得到的结果应该是 282,也许它甚至可以缩短脚本?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    $log_file 的内容与上面的示例类似。

    Get-Content $log_file | Where-Object{$_ -match "\d+(,\d+)?"} | 
        ForEach-Object{[double]($matches[0] -replace ",",".")} | 
        Measure-Object -Sum | 
        Select-Object -ExpandProperty sum
    

    用可选的逗号匹配所有具有数值的行。我假设它们可能是可选的,因为我不知道整数是如何出现的。用句号替换逗号并转换为双精度。使用测量对象,我们将所有值相加并展开结果。

    不是唯一的方法,但它很简单,可以理解发生了什么。

    您始终可以将上述内容包装在一个循环中,以便您可以将其用于多个文件。 Get-ChildItem "C:temp\" -Filter "job*" | ForEach-Object...等

    【讨论】:

      【解决方案2】:

      Matt's helpful answer 给出了简洁有效的解决方案。

      至于你尝试了什么

      至于为什么像222,6这样带有单个标记的行会导致此命令中的多个输出:

      select-string -Path $extracted_strings -Pattern $regex -AllMatches | 
        % { $_.Matches } | % { $_.Value }  > $output_numbers
      

      您的正则表达式[= ](\d{1,})没有解释症状,但只有\d{1,} 会,因为这会捕获2226单独,由于-AllMatches

      [= ](\d{1,}) 可能不符合您的要求,因为 [= ] 匹配 单个 字符,该字符可以是 或者 = 或 一个空格;使用您的示例输入,这只会匹配数字前的空格。
      按顺序匹配字符,只需将它们彼此相邻放置:= (\d{1,})

      还要注意,即使您将\d{1,} 包含在(...) 中以创建捕获组,您以后的代码实际上并没有使用该捕获组匹配的内容;仅当您需要优先级时才使用(...)(在这种情况下,您甚至可以选择不使用(?:...) 捕获子表达式)或者您确实需要访问子表达式匹配的内容。

      也就是说,您可以在此处实际使用捕获组(另一种方法是使用后向断言),它允许您同时匹配领先的 =<space> 以实现稳健性和提取只有感兴趣的数字标记(节省您稍后修剪空白的需要)。
      如果我们将\d{1,} 简化为\d+ 并附加,\d+ 以匹配逗号后的数字,我们得到:

      = (\d+,\d+)
      

      Select-String 返回的[System.Text.RegularExpressions.Match] 实例允许我们通过.Groups 属性访问捕获组捕获的内容(以下简化示例也适用于多个输入行):

      > 'totalSize = 222,6GB' | Select-String '= (\d+,\d+)' | % { $_.Matches.Groups[1].Value }
      222,6
      

      附带说明:您的代码包含大量重复,可以使用数组和管道消除这些重复;例如:

      $joblog1_file = "C:\temp\joblog1.txt"
      $joblog2_file = "C:\temp\joblog2.txt"
      $joblog3_file = "C:\temp\joblog3.txt"
      $joblog4_file = "C:\temp\joblog4.txt"
      $joblog5_file = "C:\temp\joblog5.txt"
      

      可以替换为(使用管道创建文件名的数组):

      $joblog_files = 1..5 | % { "C:\temp\joblog$_.txt" }
      

      $jobdata0 = (Get-Content -Path $joblog1_file)
      $jobdata1 = (Get-Content -Path $joblog2_file)
      $jobdata2 = (Get-Content -Path $joblog3_file)
      $jobdata3 = (Get-Content -Path $joblog4_file)
      $jobdata4 = (Get-Content -Path $joblog5_file)
      $result = $jobdata0  + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
      

      然后可以替换为(将文件名的数组传递给Get-Content):

      $result = Get-Content $joblog_files
      

      【讨论】:

      • 我没有时间浏览他的代码,因为我知道这将是结果。我很高兴有人花时间。
      • 谢谢,@Matt。我希望在我们的两个答案之间,我们现在涵盖了两组未来的读者:寻求优雅解决方案的读者和寻求解释的读者。
      • 是的。我通常在 SO 和 CR 上喜欢这种类型的事情,但我现在显然正在得到报酬来做其他事情。
      • 谢谢你们的解释,它将帮助我做更好和简化的powershellscripts。
      猜你喜欢
      • 2012-08-06
      • 2013-07-05
      • 2012-10-06
      • 2015-03-24
      • 2013-03-10
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 2019-02-17
      相关资源
      最近更新 更多