【问题标题】:Powershell regex split stringPowershell 正则表达式拆分字符串
【发布时间】:2013-11-19 18:30:47
【问题描述】:

我还是 Powershell 的新手;并且无法将我的头转换为一种将字符串转换为对 powershell 友好的对象的方法(转换为 Powershell 属性名称和每个属性名称的相应值,或者,如果这太复杂,那么,a二维阵列网格)。理想情况下,我不介意查看每种方式是如何实现的,因此我将来可以采用任何一种方式。无论如何,这是下面的字符串:

$String = 
"   Port      Name               Status       Vlan       Duplex  Speed Type
    Fa1/0/1   Router 2           connected    trunk        full    100 10/100BaseTX
    Fa1/0/2   User               connected    101          full    100 10/100BaseTX
    Fa1/0/3   User               notconnect   101          full    100 10/100BaseTX
    Fa1/0/4   Video VLAN         connected    503          full    100 10/100BaseTX
    Fa1/0/5   User               notconnect   101          full    100 10/100BaseTX
"

由于我的 Powershell 脚本编写经验非常有限,我几乎不能只做一维数组(使用 [regex]::split)。不幸的是,我使用该方法的方式导致数组中的项目为空;而且,它似乎没有像 string.split 那样的 [StringSplitOptions]"RemoveEmptyEntries" 功能。

type int.txt | %{$data = [regex]::split($_, '(\s\s)+')
Write-Output "$($data[0])`t$($data[1])`t$($data[2])`t$($data[3])`t$($data[4])`t$($data[5])`t$($data[6])`t$($data[7])"}

因此,我最终将一些不需要的空项分配给数组;输出如下:

                Port            Name             Status
                Fa1/0/1          Router 2                connected
                Fa1/0/2          User            connected
                Fa1/0/3          User            notconnect
                Fa1/0/4          Video VLAN              connected
                Fa1/0/5          User            notconnect

无论如何,我真的很想要一个专家的解决方案来演示如何将字符串转换为 Powershell 属性和/或二维数组。提前致谢!

【问题讨论】:

    标签: regex arrays string powershell properties


    【解决方案1】:

    如果您正在处理固定宽度的数据,并且不一定相信您将有可预测的分隔符进行拆分,那么您可能希望仅根据列位置对其进行解析。您会经常看到使用 string.substring() 方法完成此操作,但您可以使用正则表达式。就个人而言,我更喜欢正则表达式。这是一个例子:

    $string = 
    @'
        Port      Name               Status       Vlan       Duplex  Speed Type
        Fa1/0/1   Router 2           connected    trunk        full    100 10/100BaseTX
        Fa1/0/2   User               connected    101          full    100 10/100BaseTX
        Fa1/0/3   User               notconnect   101          full    100 10/100BaseTX
        Fa1/0/4   Video VLAN         connected    503          full    100 10/100BaseTX
        Fa1/0/5   User               notconnect   101          full    100 10/100BaseTX
    '@
    
    
    
    $regex = '(.{10})(.{19})(.{13})(.{11})(.{8})(.{6})(.+)'
    
    $string.split("`n") -notmatch '\s*Port\s+' |
     foreach {
      if ($_.trim() -match $regex)
       { [PSCustomObject]@{
         Port   = $Matches[1].trim()
         Name   = $Matches[2].trim()
         Status = $Matches[3].trim()
         Vlan   = $Matches[4].trim()
         Duplex = $Matches[5].trim()
         Speed  = $Matches[6].trim()
         Type   = $Matches[7].trim()
        }
       }
      } | ft -AutoSize
    
    
    
    Port    Name       Status     Vlan  Duplex Speed Type        
    ----    ----       ------     ----  ------ ----- ----        
    Fa1/0/1 Router 2   connected  trunk full   100   10/100BaseTX
    Fa1/0/2 User       connected  101   full   100   10/100BaseTX
    Fa1/0/3 User       notconnect 101   full   100   10/100BaseTX
    Fa1/0/4 Video VLAN connected  503   full   100   10/100BaseTX
    Fa1/0/5 User       notconnect 101   full   100   10/100BaseTX
    

    【讨论】:

    • 很好 :) 这将是我的下一次尝试,但我将使用 IndexOf("Name") 等检测索引。
    • 这非常好!效果很好!最重要的是,我了解所有代码,甚至是正则表达式。我想我现在应该可以制作类似的脚本,而不必参考这段代码 sn-p。谢谢!!
    • 太棒了!还可以使用显式捕获使其更具可读性。例如$regex = '(?<port>.{10})...',然后是Port = $Matches['port']
    【解决方案2】:

    这是另一个版本,FWIW:

    $regex = '(.{10})(.{19})(.{13})(.{11})(.{8})(.{6})(.+)'
    $props = ',Port,Name,Status,Vlan,Duplex,Speed,Type'.split(',') 
    $ht = [ordered]@{}
    
    $string.split("`n") -notmatch '\s*Port\s+' |
     foreach {
      if ($_.trim() -match $regex) {
       for ($i=1;$i -lt $props.count;$i++)
       { $ht[$props[$i]]=$matches[$i]}
       [PSCustomObject]$ht
      }
    }
    

    【讨论】:

      【解决方案3】:

      试试这个。

      $String = @"
          Port      Name               Status       Vlan       Duplex  Speed Type
          Fa1/0/1   Router 2           connected    trunk        full    100 10/100BaseTX
          Fa1/0/2   User               connected    101          full    100 10/100BaseTX
          Fa1/0/3   User               notconnect   101          full    100 10/100BaseTX
          Fa1/0/4   Video VLAN         connected    503          full    100 10/100BaseTX
          Fa1/0/5   User               notconnect   101          full    100 10/100BaseTX
      "@
      
      $temp = $string -split "`n" | 
                  Foreach {$_ -replace '(\S+) (\S+)\s*$', '$1  $2' -replace '^\s+',''}
      
      $temp | Foreach {$_ -replace '(?<=\S) (?=\S)',[char]0xA0 -replace ' +',' ' } | 
              ConvertFrom-Csv -Delimiter ' ' | Format-Table -auto
      

      这通过准备输入字符串来在最后一列和最后一列之间放置两个空格,然后用不间断空格替换剩余的单个空格。

      【讨论】:

      • 谢谢,看来您发现了我遇到的一些问题;但我没有提到。 “名称”字段肯定有空格,它们都是空格分隔的;这就是我使用正则表达式拆分方法来检测至少两个空格的原因。因此,不幸的是,您提到的内容不适用于我提供的示例。
      • 我已经更新了这个答案,不需要手动更改输入字符串。在我的测试中,它实际上会根据您指定的输入字符串输出正确的数据。
      【解决方案4】:

      让我们将其转换为 csv 文件,然后将其导入。这将为您提供一个数组,其中包含表示每个接口及其所有属性的对象。

      PS > $a = Get-Content .\int.txt | Foreach {$_.Trim() -replace ' {2,}',','}  | ConvertFrom-Csv
      
      PS > $a | ft -AutoSize
      
      Port    Name       Status     Vlan  Duplex Speed Type      
      ----    ----       ------     ----  ------ ----------      
      Fa1/0/1 Router 2   connected  trunk full   100 10/100BaseTX
      Fa1/0/2 User       connected  101   full   100 10/100BaseTX
      Fa1/0/3 User       notconnect 101   full   100 10/100BaseTX
      Fa1/0/4 Video VLAN connected  503   full   100 10/100BaseTX
      Fa1/0/5 User       notconnect 101   full   100 10/100BaseTX
      

      这也适用于接口名称中的空格,只要单词之间只有一个空格即可。

      【讨论】:

      • 谢谢,例如,我将如何查找所有状态为“已连接”的行;并获得相应的“端口”名称?编辑:没关系...明白了:$a | ?{$_.status -eq "已连接"} |选择端口
      • 这不会为包含该数据的字符串生成正确的输出,即它是单个字符串 - 正如 OP 最初提出的问题。例如$string | Foreach {$_.Trim() -replace ... 产生的输出与原始输入字符串不匹配。
      • 它也没有将 Speed 和 Type 拆分为单独的属性。
      • 我的错。没有注意到 Speed 和 Type 只用一个空格隔开。 @KeithHill,我注意到他后来使用了type int.txt,所以我猜测数据已经存储到文件中了。这里的字符串(我们可以拆分)只是为了在这里进行测试。 :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多