【问题标题】:Parse log file and turn it into a custom object解析日志文件并将其转换为自定义对象
【发布时间】:2019-09-03 14:15:12
【问题描述】:

日志文件包含:

===== SMTP_server_not_set_up @PANEL SMTP 服务器未设置。联系系统管理员。 @PAGECNT 381 @SCANADFCNT 0 @日期时间 2019-08-29T10:05:51-0400 @正常运行时间 0:00:23 @CODELVL 基础 MSNGM.053.023 发动机 GM.052.E015 面板 f0.12p48v3 @结尾 ===== 上电复位 @PAGECNT 381 @SCANADFCNT 0 @日期时间 2019-08-29T10:05:50-0400 @色情49 @CODELVL 基础 MSNGM.053.023 发动机 GM.052.E015 面板 f0.12p48v3 @结尾 ===== Load_MP_Feeder_with_Custom_Type_5_Letter @PANEL 使用自定义类型 5 字母加载 MP 进纸器 @PAGECNT 337 @SCANADFCNT 0 @日期时间 2019-08-29T09:59:22-0400 @正常运行时间 0:44:15 @CODELVL 基础 MSNGM.053.023 发动机 GM.052.E015 面板 f0.12p48v3 @结尾 =====

...还有很多行

我想抓取 "=====" 行之后的 6 行并将其转换为自定义对象。

例如,根据第一行,理想的对象是:

消息:SMTP_server_not_set_up PANEL : @PANEL SMTP 服务器未设置。联系系统管理员。 PAGECNT:@PAGECNT 381 SCANADFCNT :@SCANADFCNT 0 日期时间:@DATETIME 2019-08-29T10:05:51-0400 正常运行时间:@正常运行时间 0:00:23

到目前为止,我可以像这样隔离重复出现的“=====”后面的那 6 行:

(Select-String -Path "$PSScriptRoot\$($printer)_history.log" -Pattern "=====" -Context 6).Context.PostContext

现在我需要将这些行放入自定义对象中。我真的不知道如何正确处理这个问题,我正在探索做这样的事情:

$ObjHistory = @()
(Select-String -Path "$PSScriptRoot\$($printer)_history.log" -Pattern "=====" -Context 6).Context.PostContext | ForEach-Object {
    if ($_ -match "PANEL") {
        $properties = [ordered]@{'PANEL'="$_"}
        $objet = New-Object -TypeName PSObject -Property $properties
    }

    $ObjHistory += $objet
}

$ObjHistory

这样我发现我可以成功地将 PANEL 属性放入自定义对象中,它会输出:

控制板 ----- @PANEL SMTP 服务器未设置。联系系统管理员。 @PANEL 使用自定义类型 5 字母加载 MP 进纸器 @PANEL 使用自定义类型 5 字母加载 MP 进纸器 @PANEL 使用自定义类型 5 字母加载 MP 进纸器 @PANEL 使用自定义类型 5 字母加载 MP 进纸器

但是,我需要为其他行执行此操作,但我一直不知道如何处理它,因为 ForEach-Object 一次只执行一行。

【问题讨论】:

    标签: powershell parsing pscustomobject


    【解决方案1】:

    另一种方法

    • 使用-raw参数读取登录
    • ===== 拆分为多个部分
    • 将部分拆分为行并将这些行存储到变量中以输出为[PSCustomObject]

    编辑

    • 创建一个空的[PSCustomObject] 并使用正则表达式填充相关属性
      (如果需要,清理多余的@propname)
    ## Q:\Test\2019\09\03\SO_57773555.ps1
    $RE = [regex]'^@(MESSAGE|PANEL|PAGECNT|SCANADFCNT|DATETIME|UPTIME) +(.*)$'
    $Path = "$PSScriptRoot\$($printer)_history.log"
    
    $ObjHistory = foreach($Section in ((Get-Content $Path -raw)  -split '===== ?\r?\n' -ne '')){
        $obj = [PSCustomobject]@{MESSAGE="";PANEL="";PAGECNT="";SCANADFCNT="";DATETIME="";UPTIME=""}
        $obj.MESSAGE = ($Section -split '\r?\n')[0]
        foreach($Line in ($Section -split '\r?\n')[1..5] ){
          if($Line -match $RE){
            $obj.($Matches[1])=$Matches[2] # to have the complete line =$Matches[0]
          }
        }
        $obj 
      }
    $ObjHistory
    
    

    > .\SO_57773555.ps1                                                                                                                 
    
    MESSAGE    : SMTP_server_not_set_up
    PANEL      : SMTP server not set up. Contact system administrator.
    PAGECNT    : 381
    SCANADFCNT : 0
    DATETIME   : 2019-08-29T10:05:51-0400
    UPTIME     : 0:00:23
    
    MESSAGE    : PowerOnReset
    PANEL      :
    PAGECNT    : 381
    SCANADFCNT : 0
    DATETIME   : 2019-08-29T10:05:50-0400
    UPTIME     :
    
    MESSAGE    : Load_MP_Feeder_with_Custom_Type_5_Letter
    PANEL      : Load MP Feeder with Custom Type 5 Letter
    PAGECNT    : 337
    SCANADFCNT : 0
    DATETIME   : 2019-08-29T09:59:22-0400
    UPTIME     : 0:44:15
    

    【讨论】:

    • 这真是高手啊我的朋友。非常感激。我学到了很多东西,只是想理解每一行。这逻辑真是天才哈哈。
    • 要确定的快速问题,在 foreach($Section in...) 中,最后执行“$obj”使其每次都将对象“附加”到 $objhistory,对吗?因此将所有对象合二为一?
    • 没错,$obj 需要与 all 属性一起出现,才能填充 RegEx 提取的值。
    【解决方案2】:

    提取的数据已经具有键/值类型的结构,因此您可以使用ConvertFrom-StringData 将数据块转换为哈希表,然后再转换为自定义对象。

    避免在循环中追加到数组。只需输出循环中的对象并将循环输出收集到一个变量中。

    $path  = "${PSScriptRoot}\${printer}_history.log"
    
    $objHistory = @(Select-String -Path $path -Pattern '=====' -Context 6 | ForEach-Object {
        $props = $_.Context.PostContext -replace '^[^@]', 'MESSAGE = $&' -replace '^@(\w+)', '$1 = $&' |
                 Out-String |
                 ConvertFrom-StringData
        New-Object -Type PSObject -Property $props
    })
    

    -replace '^[^@]', 'MESSAGE = $&' 在不以@ 开头的行前面加上字符串MESSAGE =-replace '^@(\w+)', '$1 = $&'WORD = @WORD 替换行首的@WORD。随后的Out-String 然后将数据块合并为单个字符串,否则ConvertFrom-StringData 将每行创建一个哈希表。

    【讨论】:

    • 由于上述日志文件本身不包含冒号(并且第一行缺少消息标记),我的脚本出现错误。这就是为什么我走了更长的路;-)
    • @LotPings 我最初将第二个文本 sn-p 误认为是实际数据,但已经修改了我的答案。
    • 是的,我明白了,但第一行仍然从ConvertFrom-StringData 引发错误...没有正确的 Name=Value 格式
    • @LotPings 啊,是的。我忽略了第一行。感谢您的提醒。
    【解决方案3】:

    对基于示例的解析进行了转换。多个示例效果更好。

    $template = @'
    =====
    {message*:SMTP_server_not_set_up}
    {panel:@PANEL  SMTP server not set up. Contact system administrator.}
    {pagecnt:@PAGECNT  381}
    {scanadfcnt:@SCANADFCNT 0}
    {datetime:@DATETIME  2019-08-29T10:05:51-0400}
    {uptime:@UPTIME  0:00:23}
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    {message*:PowerOnReset}
    {pagecnt:@PAGECNT  381}
    {scanadfcnt:@SCANADFCNT 0}
    {datetime:@DATETIME  2019-08-29T10:05:50-0400}
    @PORCNT  49
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    {message*:Load_MP_Feeder_with_Custom_Type_5_Letter}
    {panel:@PANEL  Load MP Feeder with Custom Type 5 Letter}
    {pagecnt:@PAGECNT  337}
    {scanadfcnt:@SCANADFCNT 0}
    {datetime:@DATETIME  2019-08-29T09:59:22-0400}
    {uptime:@UPTIME  0:44:15}
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    '@
    
    $testtext = @'
    =====
    SMTP_server_not_set_up
    @PANEL  SMTP server not set up. Contact system administrator.
    @PAGECNT  381
    @SCANADFCNT 0
    @DATETIME  2019-08-29T10:05:51-0400
    @UPTIME  0:00:23
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    PowerOnReset
    @PAGECNT  381
    @SCANADFCNT 0
    @DATETIME  2019-08-29T10:05:50-0400
    @PORCNT  49
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    Load_MP_Feeder_with_Custom_Type_5_Letter
    @PANEL  Load MP Feeder with Custom Type 5 Letter
    @PAGECNT  337
    @SCANADFCNT 0
    @DATETIME  2019-08-29T09:59:22-0400
    @UPTIME  0:44:15
    @CODELVL
    
    Base MSNGM.053.023
    Engine GM.052.E015
    Panel f0.12p48v3
    @END
    =====
    '@
    
    $testText | ConvertFrom-String -TemplateContent $template
    

    输出:

    message    : SMTP_server_not_set_up
    panel      : @PANEL  SMTP server not set up. Contact system administrator.
    pagecnt    : @PAGECNT  381
    scanadfcnt : @SCANADFCNT 0
    datetime   : @DATETIME  2019-08-29T10:05:51-0400
    uptime     : @UPTIME  0:00:23
    
    message    : PowerOnReset
    pagecnt    : @PAGECNT  381
    scanadfcnt : @SCANADFCNT 0
    datetime   : @DATETIME  2019-08-29T10:05:50-0400
    
    message    : Load_MP_Feeder_with_Custom_Type_5_Letter
    panel      : @PANEL  Load MP Feeder with Custom Type 5 Letter
    pagecnt    : @PAGECNT  337
    scanadfcnt : @SCANADFCNT 0
    datetime   : @DATETIME  2019-08-29T09:59:22-0400
    uptime     : @UPTIME  0:44:15
    

    【讨论】:

    • 根据我的经验,ConvertFrom-String 由于启发式方法而不是很可靠。您对数据的描述越好/越详细,就会发生越多不可预测的错误,就在您不期望它们发生的时候。
    • 尽管这似乎不是解决问题的最佳方法,但我对发现所描述的模板/convertfrom-string 方法非常感兴趣。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多