有没有办法让 Powershell 对函数的输出使用特定格式?
Is There a Way to Cause Powershell to Use a Particular Format for a Function's Output?
我希望建议(也许强制执行,但我还不确定语义)输出的特定格式一个 PowerShell 函数。
about_Format.ps1xml(针对 PowerShell 7.1 的版本)这样说:'从 PowerShell 6 开始,默认视图在 PowerShell 源代码中定义。 PowerShell 5.1 及更早版本中的 Format.ps1xml 文件在 PowerShell 6 及更高版本中不存在。'。然后文章继续解释 Format.ps1xml 文件如何用于更改对象的显示等。这不是很明确:'不存在'-ne 'cannot exist'。 ..
这引出了几个问题:
- 虽然它们“不存在”,但 Format.ps1xml 文件可以 created/used 在高于 5.1 的 PowerShell 版本中吗?
- 无论是否可以,是否有更好的做法向 PowerShell 建议某个函数应如何格式化返回的数据?请注意,'suggest' 固有的是 必须保留 PowerShell 输出的管道性质:用户必须仍然能够将函数的输出通过管道传递给 Format-List 或 ForEach -对象等..
例如,Get-ADUser
cmdlet returns 对象由 Format-List
格式化。如果我编写一个名为 Search-ADUser
的函数在内部调用 Get-ADUser
和 returns 其中一些对象,输出也将被格式化为列表。在返回之前将输出通过管道传输到 Format-Table
不满足我的要求,因为输出将不会被视为管道中的单独对象。
示例代码:
function Search-ADUser {
param (
$Name,
[ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
)
return Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties
}
最佳答案应该同时解决这两个问题,尽管第二个问题更为突出。
不可接受的答案包括建议函数 不应 强制执行格式,and/or 用户应将函数的输出通过管道传输到他们选择的格式化程序。这是一个非常主观的立场,大多数人是否持有与问题无关。
我在发帖前搜索了 force function format #powershell-7.0
,但 none 的搜索结果似乎是相关的。
Although they 'don't exist', can Format.ps1xml
files be created/used in versions of PowerShell greater than 5.1?
是;事实上,任何第三方代码必须使用它们来定义自定义格式。
*.ps1xml
文件总是需要这样的定义是不幸的; GitHub issue #7845 asks for an in-memory, API-based alternative (which for type data already exists, via the Update-TypeData
cmdlet).
现在只有 PowerShell 附带的格式化数据被硬编码到 PowerShell(核心)可执行文件中,大概是出于性能原因。
is there some better practice for suggesting to PowerShell how a certain function should format returned data?
缺少基于 API 的方法来定义格式化数据需要以下方法:
确定应应用格式的 .NET 类型的全名。
如果格式应应用于 [pscustomobject]
个实例,您需要 (a) 选择 唯一(虚拟)类型名称(b) 通过 PowerShell 的 ETS (Extended Type System) 将其分配给 [pscustomobject]
个实例;例如:
对于 Select-Object
cmdlet 创建的 [pscustomobject]
个实例:
# Assign virtual type name "MyVirtualType" to the objects output
# by Select-Object
Get-ChildItem *.txt | Select-Object Name, Length | ForEach-Object {
$_.pstypenames.Insert(0, 'MyVirtualType'); $_
}
对于[pscustomobject]
文字,通过PSTypeName
条目指定类型名称:
[pscustomobject] @{
PSTypeName = 'MyVirtualType'
foo = 1
bar = 2
}
为该类型创建一个 *.ps1mxl
文件并将其加载到每个会话中。
如果在 模块 中定义了依赖此格式化数据的命令,您可以将该文件合并到您的模块中,以便在模块已导入。
有关创作此类文件的帮助,请参阅:
GitHub proposal #10463 要求大大简化体验,支持指定所需格式的扩展 [OutputType()]
属性。
应用于您的示例函数:
以下函数在会话中的第一次调用时为其输出类型 按需 创建一个(临时)*.ps1xml
文件,因此为了确保对所有 5 个属性应用(隐式)Format-Table
格式(默认情况下,5 个或更多属性导致(隐式)Format-List
格式)。
如您所见,为格式定义创建 XML 既冗长又麻烦,即使没有额外的设置,例如列宽和对齐方式。
更好但更精细的解决方案是将您的函数打包在 module into whose folder you can place the *.ps1mxl
file (e.g., SearchAdUserResult.Format.ps1xml
) and then instruct PowerShell to load the file on module import, via the FormatsToProcess
key in the module manifest (*.psd1
) 中 - 例如,FormatsToProcess = 'SearchAdUserResult.Format.ps1xml'
请注意,您也可以直接为 Get-ADUser
输出的 Microsoft.ActiveDirectory.Management.ADUser
实例创建 *.ps1mxl
文件,但这样做会在会话范围内应用格式化, 任何发出此类对象的命令。
function Search-ADUser {
param (
$Name,
[ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
)
# The self-chosen ETS type name.
$etsTypeName = 'SearchAdUserResult'
# Create the formatting data on demand.
if (-not (Get-FormatData -ErrorAction Ignore $etsTypeName)) {
# Create a temporary file with formatting definitions to pass to
# Update-FormatData below.
$tempFile = Join-Path ([IO.Path]::GetTempPath()) "$etsTypeName.Format.ps1xml"
# Define a table view with all 5 properties.
@"
<Configuration>
<ViewDefinitions>
<View>
<Name>$etsTypeName</Name>
<ViewSelectedBy>
<TypeName>$etsTypeName</TypeName>
</ViewSelectedBy>
<TableControl>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>Enabled</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>SamAccountName</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Name</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>emailAddress</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>proxyAddresses</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
"@ > $tempFile
# Load the formatting data into the current session.
Update-FormatData -AppendPath $tempFile
# Clean up.
Remove-Item $tempFile
}
# Call Get-ADUser and assign the self-chosen ETS type name to the the output.
# Note: To test this with a custom-object literal, use the following instead of the Get-ADUser call:
# [pscustomobject] @{ Enabled = $true; SamAccountName = 'jdoe'; Name = 'Jane Doe'; emailAddress = 'jdoe@example.org'; proxyAddresses = 'janedoe@example.org' }
Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties | ForEach-Object {
$_.pstypenames.Insert(0, $etsTypeName); $_
}
}
然后您将看到基于格式数据的所需表格输出;例如:
Enabled SamAccountName Name emailAddress proxyAddresses
------- -------------- ---- ------------ --------------
True jdoe Jane Doe jdoe@example.org janedoe@example.org
我希望建议(也许强制执行,但我还不确定语义)输出的特定格式一个 PowerShell 函数。
about_Format.ps1xml(针对 PowerShell 7.1 的版本)这样说:'从 PowerShell 6 开始,默认视图在 PowerShell 源代码中定义。 PowerShell 5.1 及更早版本中的 Format.ps1xml 文件在 PowerShell 6 及更高版本中不存在。'。然后文章继续解释 Format.ps1xml 文件如何用于更改对象的显示等。这不是很明确:'不存在'-ne 'cannot exist'。 ..
这引出了几个问题:
- 虽然它们“不存在”,但 Format.ps1xml 文件可以 created/used 在高于 5.1 的 PowerShell 版本中吗?
- 无论是否可以,是否有更好的做法向 PowerShell 建议某个函数应如何格式化返回的数据?请注意,'suggest' 固有的是 必须保留 PowerShell 输出的管道性质:用户必须仍然能够将函数的输出通过管道传递给 Format-List 或 ForEach -对象等..
例如,Get-ADUser
cmdlet returns 对象由 Format-List
格式化。如果我编写一个名为 Search-ADUser
的函数在内部调用 Get-ADUser
和 returns 其中一些对象,输出也将被格式化为列表。在返回之前将输出通过管道传输到 Format-Table
不满足我的要求,因为输出将不会被视为管道中的单独对象。
示例代码:
function Search-ADUser {
param (
$Name,
[ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
)
return Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties
}
最佳答案应该同时解决这两个问题,尽管第二个问题更为突出。
不可接受的答案包括建议函数 不应 强制执行格式,and/or 用户应将函数的输出通过管道传输到他们选择的格式化程序。这是一个非常主观的立场,大多数人是否持有与问题无关。
我在发帖前搜索了 force function format #powershell-7.0
,但 none 的搜索结果似乎是相关的。
Although they 'don't exist', can
Format.ps1xml
files be created/used in versions of PowerShell greater than 5.1?
是;事实上,任何第三方代码必须使用它们来定义自定义格式。
*.ps1xml
文件总是需要这样的定义是不幸的; GitHub issue #7845 asks for an in-memory, API-based alternative (which for type data already exists, via theUpdate-TypeData
cmdlet).
现在只有 PowerShell 附带的格式化数据被硬编码到 PowerShell(核心)可执行文件中,大概是出于性能原因。
is there some better practice for suggesting to PowerShell how a certain function should format returned data?
缺少基于 API 的方法来定义格式化数据需要以下方法:
确定应应用格式的 .NET 类型的全名。
如果格式应应用于
[pscustomobject]
个实例,您需要 (a) 选择 唯一(虚拟)类型名称(b) 通过 PowerShell 的 ETS (Extended Type System) 将其分配给[pscustomobject]
个实例;例如:对于
Select-Object
cmdlet 创建的[pscustomobject]
个实例:# Assign virtual type name "MyVirtualType" to the objects output # by Select-Object Get-ChildItem *.txt | Select-Object Name, Length | ForEach-Object { $_.pstypenames.Insert(0, 'MyVirtualType'); $_ }
对于
[pscustomobject]
文字,通过PSTypeName
条目指定类型名称:[pscustomobject] @{ PSTypeName = 'MyVirtualType' foo = 1 bar = 2 }
为该类型创建一个
*.ps1mxl
文件并将其加载到每个会话中。如果在 模块 中定义了依赖此格式化数据的命令,您可以将该文件合并到您的模块中,以便在模块已导入。
有关创作此类文件的帮助,请参阅:
GitHub proposal #10463 要求大大简化体验,支持指定所需格式的扩展 [OutputType()]
属性。
应用于您的示例函数:
以下函数在会话中的第一次调用时为其输出类型 按需 创建一个(临时)
*.ps1xml
文件,因此为了确保对所有 5 个属性应用(隐式)Format-Table
格式(默认情况下,5 个或更多属性导致(隐式)Format-List
格式)。如您所见,为格式定义创建 XML 既冗长又麻烦,即使没有额外的设置,例如列宽和对齐方式。
更好但更精细的解决方案是将您的函数打包在 module into whose folder you can place the
*.ps1mxl
file (e.g.,SearchAdUserResult.Format.ps1xml
) and then instruct PowerShell to load the file on module import, via theFormatsToProcess
key in the module manifest (*.psd1
) 中 - 例如,FormatsToProcess = 'SearchAdUserResult.Format.ps1xml'
请注意,您也可以直接为
Get-ADUser
输出的Microsoft.ActiveDirectory.Management.ADUser
实例创建*.ps1mxl
文件,但这样做会在会话范围内应用格式化, 任何发出此类对象的命令。
function Search-ADUser {
param (
$Name,
[ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
)
# The self-chosen ETS type name.
$etsTypeName = 'SearchAdUserResult'
# Create the formatting data on demand.
if (-not (Get-FormatData -ErrorAction Ignore $etsTypeName)) {
# Create a temporary file with formatting definitions to pass to
# Update-FormatData below.
$tempFile = Join-Path ([IO.Path]::GetTempPath()) "$etsTypeName.Format.ps1xml"
# Define a table view with all 5 properties.
@"
<Configuration>
<ViewDefinitions>
<View>
<Name>$etsTypeName</Name>
<ViewSelectedBy>
<TypeName>$etsTypeName</TypeName>
</ViewSelectedBy>
<TableControl>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>Enabled</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>SamAccountName</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Name</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>emailAddress</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>proxyAddresses</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
"@ > $tempFile
# Load the formatting data into the current session.
Update-FormatData -AppendPath $tempFile
# Clean up.
Remove-Item $tempFile
}
# Call Get-ADUser and assign the self-chosen ETS type name to the the output.
# Note: To test this with a custom-object literal, use the following instead of the Get-ADUser call:
# [pscustomobject] @{ Enabled = $true; SamAccountName = 'jdoe'; Name = 'Jane Doe'; emailAddress = 'jdoe@example.org'; proxyAddresses = 'janedoe@example.org' }
Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties | ForEach-Object {
$_.pstypenames.Insert(0, $etsTypeName); $_
}
}
然后您将看到基于格式数据的所需表格输出;例如:
Enabled SamAccountName Name emailAddress proxyAddresses
------- -------------- ---- ------------ --------------
True jdoe Jane Doe jdoe@example.org janedoe@example.org