将 PowerShell 中以空格分隔的输出解析为表格

3
当我运行命令时,PowerShell会返回以下输出:

Mod  Ports  Module-Type                         Model              Status
---  -----  -----------                         ------------------ ----------
1    48     2/4/8/10/16 Gbps Advanced FC Module DS-X9448-768K9     ok
2    48     2/4/8/10/16 Gbps Advanced 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 *

我尝试使用 .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中有空格。


2
如果您能提供以文本格式输出的脚本,那将非常有帮助。 - vCillusion
请勿粘贴屏幕截图链接。使用适当的代码格式并将相关文本粘贴到您的问题中。 - Bill_Stewart
1
根据输出结果,似乎您没有得到对象,但是您可以使用顶行来定义列名,第二行来确定每个列的宽度,然后使用SubString()函数来解析其他行。 - Kory Gill
2个回答

4

大多数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 *

由于您的所有数据都是左对齐的(每列都需要一个标题),因此可以从一开始确定列的布局,这意味着您甚至可以通过管道 流式传输 此表输入(假设.\Plink将每行单独输出):

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

(意思是,如果您将源表格作为多行字符串提供,则 ConvertFrom-SourceTable 命令会确定整个表格的列宽,否则它基于标题、过去和当前记录)

如果源表格采用更好的格式约定,如“非字符串数据(需要解释的数据)右对齐”(实际上由 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


3

在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+'(在连续两个或更多的空白字符处分割)也可以。然而,在您的数据中,情况并非如此,因为至少前两行数据仅在第三列和第四列之间有一个空格。

那个完美地运行了!非常感谢,只有一个问题,$match 如何分割表达式?是使用正则表达式捕获组吗? - Daniel Gómez
是的。$matches[0] 是完整匹配,大于 0 的索引是捕获组。 - Ansgar Wiechers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接