【问题标题】:Extract columns from text based table output从基于文本的表输出中提取列
【发布时间】:2017-11-16 17:28:14
【问题描述】:

qfarm /load 命令显示来自我的服务器的负载。 输出:

PS> qfarm /load

Server Name           Server Load  Load Throttling Load  Logon Mode
--------------------  -----------  --------------------  ------------------
SERVER-01             400          0                     AllowLogons
SERVER-02             1364         OFF                   AllowLogons
SERVER-03             1364         OFF                   AllowLogons
SERVER-04             1000         0                     AllowLogons
SERVER-05             700          0                     AllowLogons
SERVER-06             1200         0                     AllowLogons

我只需要显示第一列(服务器名称)和第二列(服务器负载)并循环遍历它们,以便稍后制作一些逻辑,但似乎 powershell 没有将其视为具有属性的对象:

PS> qfarm /load | Select -ExpandProperty "Server Name"
Select-Object : Property "Server Name" cannot be found.

还有其他可能吗,比如桌子什么的?

【问题讨论】:

  • 运行 (qfarm /load).GetType() 以查看 PowerShell 将其视为什么数据类型。我假设字符串。
  • @gms0ulman 所有命令行工具都返回字符串
  • gms0ulman 输出是 System.Array。
  • 非常在我的脑海中重复:stackoverflow.com/questions/29125337/…

标签: powershell properties multiple-columns


【解决方案1】:

一种方法是从命令的输出中构建对象。测试了以下内容:

#requires -version 3

# sample data output from command
$sampleData = @"
Server Name           Server Load  Load Throttling Load  Logon Mode
--------------------  -----------  --------------------  ------------------
SERVER-01             400          0                     AllowLogons
SERVER-02             1364         OFF                   AllowLogons
SERVER-03             1364         OFF                   AllowLogons
SERVER-04             1000         0                     AllowLogons
SERVER-05             700          0                     AllowLogons
SERVER-06             1200         0                     AllowLogons
"@ -split "`n"

$sampleData | Select-Object -Skip 2 | ForEach-Object {
  $len = $_.Length
  [PSCustomObject] @{
    "ServerName"         = $_.Substring(0,  22).Trim()
    "ServerLoad"         = $_.Substring(22, 13).Trim() -as [Int]
    "LoadThrottlingLoad" = $_.Substring(35, 22).Trim()
    "LogonMode"          = $_.Substring(57, $len - 57).Trim()
  }
}

在您的情况下,您应该能够用您的 qfarm load 命令替换 $sampleData;例如:

qfarm /load | Select-Object -Skip 2 | ForEach-Object {
...

当然,这是假设输出中没有空行,并且我的每个项目开头的列位置是正确的。

PowerShell 版本 2 等效项:

#requires -version 2

function Out-Object {
  param(
    [Collections.Hashtable[]] $hashData
  )
  $order = @()
  $result = @{}
  $hashData | ForEach-Object {
    $order += ($_.Keys -as [Array])[0]
    $result += $_
  }
  New-Object PSObject -Property $result | Select-Object $order
}

# sample data output from command
$sampleData = @"
Server Name           Server Load  Load Throttling Load  Logon Mode
--------------------  -----------  --------------------  ------------------
SERVER-01             400          0                     AllowLogons
SERVER-02             1364         OFF                   AllowLogons
SERVER-03             1364         OFF                   AllowLogons
SERVER-04             1000         0                     AllowLogons
SERVER-05             700          0                     AllowLogons
SERVER-06             1200         0                     AllowLogons
"@ -split "`n"

$sampleData | Select-Object -Skip 2 | ForEach-Object {
  $len = $_.Length
  Out-Object `
    @{"ServerName"         = $_.Substring(0,  22).Trim()},
    @{"ServerLoad"         = $_.Substring(22, 13).Trim() -as [Int]},
    @{"LoadThrottlingLoad" = $_.Substring(35, 22).Trim()},
    @{"LogonMode"          = $_.Substring(57, $len - 57).Trim()}
}

【讨论】:

  • 在前面进行类型转换并出错而不是 -as [type] 并最终得到一个空属性不是更好的做法吗?
  • 可能;这是一个取决于命令输出的判断调用。
  • 很遗憾我的版本是 2。PSCustomObject 无法识别。
  • 你还记得this比尔吗?当然这也可以在这里工作
  • @Bill_Stewart 是的,这也适用于 v2。现在我试图理解所有的代码,所以我可以只提取服务器名称列和服务器负载列,这样我就可以将它们放入数组中,然后用它们进行逻辑。
【解决方案2】:

您可以使用PowerShell Gallery 中的ConvertFrom-SourceTable cmdlet 轻松地将表转换为 PowerShell 对象:

$sampleData = ConvertFrom-SourceTable @"
Server Name           Server Load  Load Throttling Load  Logon Mode
--------------------  -----------  --------------------  ------------------
SERVER-01             400          0                     AllowLogons
SERVER-02             1364         OFF                   AllowLogons
SERVER-03             1364         OFF                   AllowLogons
SERVER-04             1000         0                     AllowLogons
SERVER-05             700          0                     AllowLogons
SERVER-06             1200         0                     AllowLogons
"@

然后选择您的列,例如:

PS C:\> $SampleData | Select-Object "Server Name", "Server Load"

Server Name Server Load
----------- -----------
SERVER-01   400
SERVER-02   1364
SERVER-03   1364
SERVER-04   1000
SERVER-05   700
SERVER-06   1200

详情见:ConvertFrom-SourceTable -?

ConvertFrom-SourceTable cmdlet 可从 PowerShell 库下载,源代码可从 GitHub iRon7/ConvertFrom-SourceTable 存储库下载。

【讨论】:

    【解决方案3】:

    命令行实用程序将其输出作为字符串数组返回。这应该有效:

    qfarm /load | ForEach-Object { $_.Substring(0,33) }
    

    【讨论】:

      【解决方案4】:

      I have answered something very similar to this in the past。我对此有一个更大的功能,但正如您在示例中所展示的那样,简化了左对齐字符串表的工作。有关详细说明,请参阅链接的答案。

      function ConvertFrom-LeftAlignedStringData{
          param (
              [string[]]$data
          )
      
          $headerString = $data[0]
          $headerElements = $headerString -split "\s{2,}" | Where-Object{$_}
          $headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}
      
          $results = $data | Select-Object -Skip 2  | ForEach-Object{
              $props = @{}
              $line = $_
              For($indexStep = 0; $indexStep -le $headerIndexes.Count - 1; $indexStep++){
                  $value = $null            # Assume a null value 
                  $valueLength = $headerIndexes[$indexStep + 1] - $headerIndexes[$indexStep]
                  $valueStart = $headerIndexes[$indexStep]
                  If(($valueLength -gt 0) -and (($valueStart + $valueLength) -lt $line.Length)){
                      $value = ($line.Substring($valueStart,$valueLength)).Trim()
                  } ElseIf ($valueStart -lt $line.Length){
                      $value = ($line.Substring($valueStart)).Trim()
                  }
                  $props.($headerElements[$indexStep]) = $value    
              }
              New-Object -TypeName PsCustomObject -Property $props
          } 
      
          return $results
      }
      
      
      $qfarmOutput = qfarm /load
      ConvertFrom-LeftAlignedStringData $qfarmOutput | select "Server Name","Server Load"
      

      此方法基于标头字段的位置。没有什么是硬编码的,它都是基于这些索引和字段名称自定义构建的。使用这些$headerIndexes,我们分割每一行并将结果(如果存在)放入其各自的列中。有逻辑可以确保我们不会尝试抓取可能不存在的字符串的任何部分并将最后一个字段视为特殊字段。

      结果

      Server Name Server Load
      ----------- -----------
      SERVER-01   400        
      SERVER-02   1364       
      SERVER-03   1364       
      SERVER-04   1000       
      SERVER-05   700        
      SERVER-06   1200       
      

      【讨论】:

      • 我认为Server Load 属性应该是[Int] 类型。
      • 是的。这可能很有用,但取决于您想要什么,您可以依赖 LHS 输入或在进行计算时强制输入。这种逻辑肯定有用,但超出了我的功能范围。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 1970-01-01
      相关资源
      最近更新 更多