【问题标题】:Parsing PowerShell space-separated output as a table将 PowerShell 空格分隔的输出解析为表格
【发布时间】:2018-07-11 14:56:18
【问题描述】:

PowerShell 在运行命令时返回此输出:

Mod Ports 模块类型模型状态 --- ----- ------------ ------ ---------- 1 48 2/4/8/10/16 Gbps 高级 FC 模块 DS-X9448-768K9 正常 2 48 2/4/8/10/16 Gbps 高级 FC 模块 DS-X9448-768K9 正常 3 0 Supervisor Module-3 DS-X97-SF1-K9 ha-standby 4 0 管理模块-3 DS-X97-SF1-K9 激活 *

我在尝试拆分它时遇到了麻烦,因为它不是制表符分隔的,而是空格分隔的,因此当我使用 .split("") 时,Module-Type 列的分隔不正确,因为它的内容也有空格。

我是新来的,任何帮助将不胜感激。

这是我的代码:

$RS = .\Plink server@puttysession -pw "password" "command"

foreach ($RSln in $RS){
    $arr = $RSln.split('',[System.StringSplitOptions]::RemoveEmptyEntries)
    Write-Host $RSln "|" $arr.Length
}

这给了我创建数组的行和长度,前两行是长度为5的数组,第三和第四行有9个项目,因为ModuleType中的空格

【问题讨论】:

  • 如果您可以为脚本提供文本格式的输出将会很有帮助。
  • 请不要将链接粘贴到屏幕截图。使用正确的代码格式并将相关文本粘贴到您的问题中。
  • 根据输出,似乎没有你正在取回对象,但你可以使用第一行定义列名,第二行确定每列的宽度,然后 SubString()解析其他行。

标签: powershell


【解决方案1】:

大多数 PowerShell 脚本编写者可能会说,使用任何类似于 Format-Table 输出的文本表作为输入是个坏主意,但我喜欢挑战这一点,因为格式化的源表可以被人类阅读,而且人类可以阅读他们并确定例如关于列开始和结束的布局,程序应该能够做同样的事情......

在某些情况下(例如 StackOverflow 站点上的示例数据),它甚至可能比 CSV 等其他格式更方便(难以阅读且仅支持字符串数据) ,XML非常冗长,因此也难以阅读)或JSON(或PSON更适用于复杂数据,但不适用于对象列表包含简单的原生属性)

基于这个想法,我编写了一个可重复使用的 ConvertFrom-SourceTable cmdlet,可在 PowerShell Gallery 下载,源代码来自 GitHub iRon7/ConvertFrom-SourceTable 存储库。

在您的情况下,将表格转换为对象的命令就像$RS | ConvertFrom-SourceTable 一样简单,让我证明一下:

$RS = '
    Mod Ports Module-Type                        Model              Status
    --- ----- ---------------------------------- ------------------ -----------
    1   48    2/4/8/10/16 Gbps Advance FC Module DS-X9448-768K9     ok
    2   48    2/4/8/10/16 Gbps Advance FC Module DS-X9448-768K9     ok
    3   0     Supervisor Module-3                DS-X97-SF1-K9      ha-standby
    4   0     Supervisor Module-3                DS-X97-SF1-K9      active *
'

$RS | ConvertFrom-SourceTable

Status      : ok
Model       : DS-X9448-768K9
Ports       : 48
Mod         : 1
Module-Type : 2/4/8/10/16 Gbps Advance FC Module

Mod         : 2
Module-Type : 2/4/8/10/16 Gbps Advance FC Module
Ports       : 48
Model       : DS-X9448-768K9
Status      : ok

Mod         : 3
Module-Type : Supervisor Module-3
Ports       : 0
Model       : DS-X97-SF1-K9
Status      : ha-standby

Mod         : 4
Module-Type : Supervisor Module-3
Ports       : 0
Model       : DS-X97-SF1-K9
Status      : active *

并且由于您的所有数据都是左对齐的(并且每一列都需要一个标题),因此可以从一开始就确定列布局,这意味着您甚至可以这个表输入(假设@ 987654334@通过管道分别输出每一行:

.\Plink server@puttysession -pw "password" "command" | ConvertFrom-SourceTable

(意思是如果您将源表作为多行字符串提供,ConvertFrom-SourceTable cmdlet 将确定整个表的列女巫,否则它基于表头、过去和当前记录)子>

如果源表有更好的格式约定,例如“非字符串数据(需要解释的数据)右对齐”(实际上由 Format-Table 完成)。您可以假设(就像@Ansgar Wiechers 所做的那样)ModPorts 是整数一个事实。例如:

$Object = ConvertFrom-SourceTable '
    Mod Ports Module-Type                        Model          Status
    --- ----- -----------                        -----          ------
      1    48 2/4/8/10/16 Gbps Advance FC Module DS-X9448-768K9 ok
      2    48 2/4/8/10/16 Gbps Advance FC Module DS-X9448-768K9 ok
      3     0 Supervisor Module-3                DS-X97-SF1-K9  ha-standby
      4     0 Supervisor Module-3                DS-X97-SF1-K9  ha-standby
'

(请注意,在此示例中,$Object | Format-Table 将提供与输入相同的输出)

更多示例请使用帮助:Help ConvertFrom-SourceTable -Full

【讨论】:

    【解决方案2】:

    在 PowerShell 中处理此类输入的推荐方法是将其解析为自定义对象。您可以使用正则表达式来做到这一点:

    $RS | Where-Object {
        $_ -match '(\d+)\s+(\d+)\s+(.*?)\s+([a-z]+(?:-[a-z0-9]+){2,})\s+(.*)'
    } | ForEach-Object {
        New-Object -Type PSObject -Property @{
            'Mod'        = [int]$matches[1]
            'Ports'      = [int]$matches[2]
            'ModuleType' = $matches[3]
            'Model'      = $matches[4]
            'Status'     = $matches[5]
        }
    }
    

    或(如果列是固定宽度)通过在定义的偏移处提取子字符串:

    $RS | Select-Object -Skip 2 | ForEach-Object {
        New-Object -Type PSObject -Property @{
            'Mod'        = [int]$_.Substring(0, 3).Trim()
            'Ports'      = [int]$_.Substring(5, 5).Trim()
            'ModuleType' = $_.Substring(12, 35).Trim()
            'Model'      = $_.Substring(48, 18).Trim()
            'Status'     = $_.Substring(67).Trim()
        }
    }
    

    需要Select-Object -Skip 2 才能跳过两个标题行。

    如果所有列通过-split '\s\s+' 由多个空格分隔(分割为 2 个或更多连续的空白字符)也可以。但是,您的数据并非如此,因为至少前 2 个数据行在第 3 列和第 4 列之间只有一个空格。

    【讨论】:

    • 效果很好!非常感谢,再问一个问题,$match 如何拆分表达式?通过使用正则表达式捕获组?
    • 是的。 $matches[0] 是完全匹配,0 以上的索引是捕获组。
    猜你喜欢
    • 2020-08-28
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多