顺便说一句:对于给定的实用程序,您可以
通过using its options to emit a machine-parseable data format而不是漂亮的打印文本(例如JSON)解决源的问题,PowerShell可能能够将其解析为对象(例如@ 987654324@),然后您可以轻松地从中选择属性并使用 PowerShell 的 Format-* cmdlet 显示格式。
在 PowerShell 中解析和转换固定宽度的柱状文本输出:
通常,最好将输入文本解析为自定义对象([pscustomobject] 实例),这使得它们适合进一步的编程处理以及使用 PowerShell 灵活的重新格式化 Format-* cmdlet。
在没有预先知识的情况下,这种解析可以在多大程度上自动化取决于给定实用程序的固定宽度输出的具体情况(假设应该修剪每列中的尾随空格):
在 kubectl 的情况下,列由 2 个空格分隔,而列名仅包含 1 个空格,并且值为 none(在您的示例输出中),因此先决条件满足自动解析。
自定义函数Select-Column,源码如下,可以通过正则表达式 +作为列分隔符表达式(2个或多个空格)将文本解析成自定义对象([pscustomobject]实例),得到的对象为适合在 PowerShell 中进行进一步的编程处理和格式化。
您可以简单地将kubectl 输出传递给它。
最多有 4 个选定列,生成的对象被隐式格式化为表格(隐含Format-Table);有 5 个或更多,作为列表(隐式 Format-List)。
您可以通过管道显式传递给Format-* cmdlet 以控制格式。
# Parse all columns except 'Age' into custom objects and format them as a table:
kubectl ... | Select-Column ' +' -Exclude Age | Format-Table
以上产出:
NAME READY STATUS RESTARTS IP NODE NOMINATED NODE READINESS GATES
---- ----- ------ -------- -- ---- -------------- ---------------
me-pod-name 2/2 Running 0 10.0.0.10 node1 <none> <none>
me-pod-name-2 1/1 Running 0 10.0.1.20 node2 <none> <none>
me-pod-name-3 1/1 Running 0 10.0.0.30 node3 <none> <none>
您可以将此类调用包装在自定义函数中,包括到Format-Table 的管道,但请注意,执行后者意味着输出再次不适合编程处理,因为Format-* 调用输出格式化指令,不是数据。
如果您希望输出为表格格式默认情况下,即使对于 5 个或更多属性,也需要做更多工作:您必须为输出对象并为该类型定义formatting data。
Select-Column源码:
function Select-Column {
<#
.SYNOPSIS
Parses columnar text data into custom objects.
.DESCRIPTION
Parses line-based columnar text data into custom objects, based on a
column-separator pattern specified as a regular expression.
By default, the values from all columns are returned as properties of the output
objects.
Use -Name to extract only given columns, or -ExcludeName to exclude columns.
.PARAMETER SeparatorPattern
A regular expression specifying what text separates the column names / values
in the input text.
The default is ' +', i.e. any run of one or more spaces, which works with
fixed-width columns whose column names and values have no embedded spaces.
.PARAMETER Name
The names of one more columns to extract from the input text.
These names must match existing columns.
By default, all columns are returned.
.PARAMETER Exclude
The names of one more input columns to exclude from the properties of the output
objects.
These names must match existing columns.
.PARAMETER InputObject
This parameter receives the individual lines of input text via the pipeline.
the pipeline.
Alternatively, you can pass the input text as a single multi-line string, both
via the pipeline and directly to this parameter.
.EXAMPLE
'col1 col2', 'val1 val2' | Select-Column
Converts the line-by-line input into custom objects with properties 'col1'
and 'col2' whose values are 'val1' and 'val2', based on runs of 1 or more
spaces acting as separators.
.EXAMPLE
'col 1 col 2', 'val1 val2' | Select-Column ' {2,}' -Exclude 'col 2'
Converts the line-by-line input into custom objects based on two or more spaces
as separators, excluding values from column 'col 2'.
#>
[CmdletBinding(DefaultParameterSetName = 'All')]
param(
[Parameter(Position = 0)]
[Alias('s')]
[string[]] $SeparatorPattern = ' +'
,
[Parameter(ParameterSetName = 'Include', Position = 1)]
[Alias('n')]
[string[]] $Name
,
[Parameter(ParameterSetName = 'Exclude')]
[Alias('x')]
[string[]] $ExcludeName
,
[Parameter(Mandatory, ValueFromPipeline)]
[string] $InputObject
)
begin {
Set-StrictMode -Version 1
$lineIndex = 0
}
process {
foreach ($line in $InputObject -split '\r?\n' -ne '') {
# Split the line into colum names / fields.
$fields = $line -split $SeparatorPattern
# Process the header row
if ($lineIndex++ -eq 0) {
$ndx = 0
# Map column names to their indices.
$nameToIndex = @{ }
foreach ($n in $fields) {
$nameToIndex[$n] = $ndx++
}
# Based on the given column names, build a list of indices
# to extract, and create an ordered hashtable with the specified
# column names to serve as a template for the output objects.
$unknownName = $null
if ($Name) {
# only the specified columns
$unknownName = (Compare-Object -PassThru $fields $Name).Where({ $_.SideIndicator -eq '=>' })
}
elseif ($ExcludeName) {
# all but the specified columns
$Name, $unknownName = (Compare-Object -PassThru $fields $ExcludeName).Where({ $_.SideIndicator -eq '<=' }, 'Split')
}
else {
$Name = $fields # default to all columns
}
if ($unknownName) { Throw "Unknown column name(s): $unknownName" }
if (-not $Name) { Throw "You have selected no output columns." }
$oht = [ordered] @{ }
$outColIndices = foreach ($n in $Name) {
$oht[$n] = $null
$nameToIndex[$n] # output the index
}
}
# Process a data row.
else {
# Fill in the ordered hashtable with this line's field values...
$ndx = 0
foreach ($n in $Name) {
$oht[$n] = $fields[$outColIndices[$ndx++]]
}
# ... and construct and output a custom object from it.
[pscustomobject] $oht
}
}
}
}