为什么在一行中运行两个 PowerShell 命令时输出格式会发生变化?

Paw*_*wik 6 powershell formatting

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

在这里,我只是尝试获取时间戳和用户在 AD 中所属的组列表,作为单行。

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

Get-ADPrincipalGroupMembership username | Select-Object name

name
----
Domain Users
CL-Inventory-Group
...
Run Code Online (Sandbox Code Playgroud)

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

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

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

name : Domain Users


name : CL-Inventory-Group


...
Run Code Online (Sandbox Code Playgroud)

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

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

mkl*_*nt0 7

tl;博士

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

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

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

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

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

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


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

如果管道包含多种类型,则默认情况下产生的特定格式取决于

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

有关详细信息,请参阅下一节。

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

Get-Date; Get-ADPrincipalGroupMembership username | Select name | Format-Table
Run Code Online (Sandbox Code Playgroud)

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


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], [UInt32], [Int64], [UInt64], [IntPtr], [UIntPtr], [Char], [Double],[Single]
  • 类型[decimal], [bigint],[string][securestring]
  • 任何其他无属性类型。

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

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

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

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

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

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

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

    • 警告:如果Format-Table被锁定,则第一个非原始对象单独确定显示为表列的属性集,如果后续对象没有这些属性,则可能导致后续对象看似消失- 这些对象仍在输出流中,但是,它们只是没有显示- 请参阅此答案以进行演示。

      • 附带说明:从 PSv5 开始,隐式使用会Format-Table导致异步行为,这可能令人惊讶;看到这个答案
    • 如果它被Format-List锁定,则不会“丢失”任何信息,因为每个对象的属性随后会在它们自己的行上单独列出。