【问题标题】:Why is the output format changed when running two PowerShell commands in one line?为什么在一行中运行两个 PowerShell 命令时输出格式会发生变化?
【发布时间】:2020-02-02 07:21:51
【问题描述】:

执行两个用分号分隔的 PowerShell 命令时,我得到了意想不到的结果。第二个命令的输出发生变化。如果我以相反的顺序运行它们,我看不到第二个命令输出。

在这里,我只是想获得一个时间戳和一个用户在 AD 中所属的组列表,作为单行。

如果我运行这一行,我会得到以下输出:

Get-ADPrincipalGroupMembership username | Select-Object name

name
----
Domain Users
CL-Inventory-Group
...

但是,如果我运行以下命令,此行为会发生变化:

get-date; Get-ADPrincipalGroupMembership username | Select-Object name

Wednesday, April 3, 2019 2:31:35 PM

name : Domain Users


name : CL-Inventory-Group


...

更奇怪的是,如果我反向运行,即我在第一个命令之后说 get-date,则在列出组后,日期戳永远不会出现。

我是否不正确地分隔命令?

【问题讨论】:

  • 我认为这与 PowerShell 在控制台中自动格式化输出有关,因为如果将其更改为 $a = Get-ADPrincipalGroupMembership username | Select-Object name; $b = get-date,则两个变量都将具有正确的数据。这可能是因为get-date 以非表格格式输出,而其他命令以表格格式输出。如果您将两者都通过管道传输到 Format-TableFormat-List,您可以同时看到两者。

标签: powershell formatting


【解决方案1】:

tl;dr

在提示符下(交互地)提交多个;-separated 命令仍然会将它们的输出发送到一个单个管道(您可以将每个提交的命令行视为一个隐式脚本文件)。

简而言之:在您的情况下,第一个命令输出的自动显示格式也决定了第二个命令的显示格式,所以哪个命令最重要

  • get-date; Get-ADPrincipalGroupMembership username | Select-Object name

    • Get-Date 之后的所有输出中都锁定了Format-List 的隐式使用,这解释了Get-ADPrincipalGroupMembership ... 命令的each-property-on-its-own 行输出。
  • 如果我反向运行,即我在第一个命令之后说 get-date,则在列出组后,日期戳永远不会出现。

    • Select-Object 输出类型为 [pscustomobject] 的实例,由于在这种情况下它们只有 1 个属性,因此锁定在 表格 显示中,即隐式使用 Format-Table选择的属性作为唯一的列,即这里只是Name。由于Get-Date 输出的[datetime] 类型没有Name 属性,所以Get-Date 的输出实际上是不可见

继续阅读背景信息和完整规则。


PowerShell 的默认显示格式针对相同类型的对象进行了优化,这是典型情况。

如果管道包含混合类型,则默认生成的特定格式取决于

  • 管道中对象的顺序
  • 及其默认的格式化行为

详情请参阅下一节。

您可以使用显式的Format-* 调用来控制格式;在您的情况下,您可以在第二个命令中使用 Format-Table 来强制表格输出:

Get-Date; Get-ADPrincipalGroupMembership username | Select name | Format-Table

警告Format-* cmdlet 的输出是格式化指令而不是原始数据,这使得该输出不合适用于进一步程序化处理


PowerShell 如何在同一管道中格式化不同类型的对象以供显示:

在没有显式格式化命令(Format-TableFormat-List、...)的情况下,PowerShell 会自动选择合适的显示格式基于给定对象的类型

  • 如果给定类型存在,PowerShell 使用预定义格式说明(请参阅Get-Help about_Format.ps1xml
  • 在他们缺席的情况下:
    • 如果类型是原始类型(见下文):输出对象的.ToString() 表示。
    • 否则:根据以下简单规则选择格式样式:4 个或更少的属性? ->Format-Table; 5个或更多? -> Format-List.

注意:Primitive在这里泛指:

  • 所有原始 CLR 类型 - 那些 .IsPrimitive 返回 $true 的类型,即
    [Boolean][Byte][SByte][Int16][UInt16][Int32]、@98765453 ,[Int64],[UInt64],[IntPtr],[UIntPtr],[Char],[Double],[Single]
  • 类型[decimal][bigint][string][securestring]
  • 任何其他无属性类型。

如果管道中的所有对象都属于同一类型,则上述定义适用于所有对象。

相比之下,如果管道中有混合类型,则适用以下逻辑:

  • 原始类型的任何实例总是打印相同,即作为它们所是的单个值的表示(不是作为一个对象带有属性),通过调用他们的.ToString()方法获得;例如,123.0hi;原始类型与管道中后续对象的格式无关。

  • 管道中的第一个非原始对象

    • 本身是根据其预定义的格式说明或上述默认规则(基于属性的数量)打印的。

    • 锁定格式样式 - 列表与表格 - 用于所有剩余的非原始对象

      • 如果对象本身隐式使用Format-TableFormat-List,那么所有剩余的非原始对象也会如此。
      • 如果对象隐式使用 Format-Custom(例如,在 Get-Date 的情况下,通过预定义的格式),则锁定的是 Format-List
  • 所有后续的非原始对象都使用锁定格式样式。

    • 警告:如果Format-Table被锁定,第一个非原始对象单独决定集合的属性显示为表格列,如果没有这些属性,可能会导致后续对象看似消失 - 这些对象仍然存在然而,在输出流中,它们只是没有显示 - 请参阅this answer 进行演示。

      • 附带说明:从 PSv5 开始,隐式使用 Format-Table 会导致可能令人惊讶的异步行为;见this answer
    • 如果锁定的是 Format-List,则不会“丢失”任何信息,因为每个对象的属性都会在各自的行中单独列出。

【讨论】:

    【解决方案2】:

    当 powershell 控制台格式化程序看到多种类型的对象时,它将默认基于第一个元素输出。对于Date,它作为一个列表输出,对于从Select-Object 输出的自定义对象,它是一个表格。输出的格式取决于对象本身的类型(请参阅帮助About Format.ps1xml)。您可以使用Format-TableSelect-Object 的输出强制为表格:

    get-date; Get-ADPrincipalGroupMembership username | Select-Object name | Format-Table
    

    【讨论】:

    • 知道为什么以相反的顺序运行命令(最后获取日期)不显示日期吗?
    • @PawełCzopowik 我不确定,我认为这是因为格式化程序已经决定它看到一堆 PSCustomObjects,所以它显示为表格,然后它得到一个没有名称属性的对象,所以它不显示它。
    • zdan 这是一个有趣的理论,我可能需要测试 :)
    • 它本身不是第一个对象,它是第一个 non-primitive 对象,它锁定了其余非原始对象的格式。 Get-Date 实际上隐式使用Format-Custom,它锁定(隐式)Format-List 用于剩余的非原始对象。
    • @PawełCzopowik:zdan 的猜测是正确的:Select-Object 输出类型为 [pscustomobject] 的实例,由于在这种情况下具有 4 个或更少的属性,因此锁定在 tabular显示,并选择特定的属性,即Name。由于Get-Date 输出的[datetime] 类型没有Name 属性,所以Get-Date 的输出实际上是不可见的。
    猜你喜欢
    • 2015-09-14
    • 2020-08-24
    • 2018-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多