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

gijlo24d  于 2023-06-29  发布在  Shell
关注(0)|答案(2)|浏览(137)

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中有空格

kyvafyod

kyvafyod1#

大多数PowerShell脚本编写者可能会说,使用任何类似于Format-Table输出的文本表作为输入是一个坏主意,但我喜欢挑战这一点,因为格式化的源表可以被人类读取,如果人类可以读取它们并确定例如。关于一列开始和结束的布局,程序应该能够做同样的事情…
在某些情况下(例如对于X1 E0 F1 X站点上的样本数据),它甚至可能比其它格式更方便,例如X1 M1 N1 X(* 难以读取并且仅支持字符串数据 )、X1 M2 N1 X( 非常冗长并且因此也难以读取 )或X1 M3 N1 X(或X1 E1 F1 X, 对于复杂数据更好,但对于包含简单本机属性的对象列表不好 *)
基于这个想法,我编写了一个可重用的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 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
'

(note在这个例子中,$Object | Format-Table将给予与输入相同的输出)
有关更多示例,请使用帮助:Help ConvertFrom-SourceTable -Full

pxiryf3j

pxiryf3j2#

在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需要跳过2个标题行。
如果所有列都被多个空格分隔,通过-split '\s\s+'(以2个或更多连续的空格字符分隔)也可以工作。但是,对于您的数据,情况并非如此,因为至少前2个数据行在第3列和第4列之间只有一个空格。

相关问题