默认格式

执行Get-Process命令,你会发现每个列头都有固定的宽度、别名等。那这些结果是否来自于某些配置文件?你可以在安装PowerShell的路径下找到其中一个名为“.format.pslxml”的文件。其中进程对象的格式化目录在“DotNetTypes.format.pslxml”中。
image.png
格式化系统对特定的对象类型,查找相应的预定义视图。下面以Get-Process命令为例,来进行分析。

查看配置文件

注意,不要保存对配置文件的任何变更。该文件带有数字签名,即使一个简单的回车或者空格,都会影响签名并阻止PowerShell从中获取信息。
1)切换至PowerShell安装目录,并打开“DotNetType.format.pslxml”文件

  1. PS D:\> cd $PSHOME
  2. PS C:\Windows\System32\WindowsPowerShell\v1.0> notepad .\DotNetTypes.format.ps1xml

2)查看Get-Process的类型:System.Diagnostics.Process
image.png
3)在配置文件查找Get-Process的类型
image.pngimage.png
记事本中显示的是以默认形式显示一个进程的管理目录,下面是表视图的定义 - TableControl
进程显示在一个多列的表中,从中可以看到熟悉的列名称。如果再往下一点点,还能发现用于定义列与属性对应关系的信息,还能看到列宽度及别名的定义等。
image.png

执行步骤

当运行“Get-Process”,在Shell中会发生下面的事情:
1)Cmdlet把类型为“System.Diagnostics.Process”的对象放入管道。
2)在管道的末端是一个名称为“Out-Default”的隐藏Cmdlet。

  • 该Cmdlet的作用是把需要运行的命令全部放入管道中,注意此Cmdlet总会存在

3)“Out-default”将对象传输到“Out-Host”,原因是PowerShell控制台默认把输出结果显示到机器所在的显示屏上(称为host)。
4)大部分以Out-开头的Cmdlets不适合用在标准对象中,而主要用于特定格式化指令上。所以当“Out-Host”发现标准对象时,会把它们传递给格式化系统。
5)格式化系统以其内部格式化的规则,检查对象的类型。然后用这些规则产生格式化指令,最终传回“Out-Host”。
6)一旦“Out-Host”发现已经生成了格式化指令,就会根据该指令生成显示到屏幕上的结果。

上面提到的内容也会在手动指定“Out-Cmdlet”时候发生。比如在运行“Get-Process | Out-File procs.txt”和“Out-File”时,PowerShell会看到你发送了一些普通对象。它会把这些对象发给格式化系统,然后创建格式化指令后回传给“Out-File”。“Out-File”基于这些指令创建格式化后的文本文件。所以在需要把对象转换成用户可读的文本输出格式时,格式化系统就会起到作用。

在上面5个步骤中,PowerShell依赖于什么格式化规则?其中一个规则是系统会检查对象类型,是否已经被预定义视图处理过。也就是在“DotNetType.format. ps1xml”中所见到的:一个针对-process对象的预定义视图。
PowerShell中还预装了其他的“.format.ps1xml”文件,这些文件在Shell启动时会被自动加载。当然,也可以创建自己的预定义视图。

默认显示属性集

如果没找到对应的视图会发生什么?比如运行:
image.png
上述命令的类型,无法在“.format.ps1xml”文件中查询到该名称。那接下来会如何?
格式化系统会查找是否有人为该对象类型预定义默认显示属性集。这些可以在另外一个配置文件Types.ps1xml中找到。将其打开后,使用查找功能定位关键字“Win32_OperatingSystem”。一旦找到它之后,就可以看到“DefaultDisplayPropertySet”。
image.png
仅仅显示6个属性是由于它们来自于默认的“Types.ps1xml”文件。如果格式化系统找到一个默认显示属性集,那么格式化系统会使用这些属性为下一步做准备;如果没有找到,那么下一步将考虑对象全部的属性值。

如果格式化系统显示4个或以下的属性,输出结果会以表格形式展现。如果有5个或以上的属性,输出结果会使用列表形式。这就是“Win32_OperatingSystem”对象的结果不以表格显示的原因。

格式化输出

在PowerShell中,有4个用于格式化的Cmdlets:Format-Table、Format-List、Format-Wide

格式化表格:Format-Table

查看“Format-Table”的帮助文档,有几个常用参数:

  • -autoSize:通过添加该参数,可以强制结果集仅保存足够的列空间,使得表格更加紧凑。
    • 通常情况下,PowerShell会根据窗口宽度生成表格
    • 除非存在一个预定义视图,如针对进程的预定义视图,在该视图中定义了列宽度。
  • -property:该参数接收一个逗号分隔符列表,一般为期望显示的属性值。
    • 该参数也接受通配符,可以使用“”代表的所有属性,或者使用“c”标识所有以c开头的属性名称。
    • 该参数是位置参数,所以可以不提供参数名称,只需要在第一个参数位置提供属性列表即可。
    • image.png
  • -groupBy:该参数会将指定的属性,按属性值进行分类,生成集合后展示。
    • 该参数只有第一次对某个对象的特定属性排序时才能生效。
    • image.png
  • -wrap:如果Shell需要把列的信息截断,会在列尾带上省略号(…)以便标识信息被截断。
    • Get-Service | Format-Table Name, Status, DisplayName -AutoSize -Wrap

      格式化列表:Format-List

      “Format-List”(别名fl)与“Format-Table”有一些相同的参数,包括“-property”。
      实际上,Fl是另一个用于展示对象属性的方式,和Gm不同。Fl也同样显示这些属性的值,以便你可以看到每个属性包含的信息。
      image.png

      格式化宽列表:Format-Wide

      “Format-Wide”(别名Fw),用于展示一个宽列表。但它仅能展示一个属性的值,所以它的“-property”参数仅接受一个属性名称,而不是接受列表,并且不接受通配符

默认情况下,“Format-Wide”会查找对象的“Name”属性,因为“Name”是广泛使用的属性并且通常包含有用信息。该命令默认输出结果只有两列。

  • -column:该参数可以用于指定输出更多的列。
  • Get-Process | Format-Wide Name -Column 5

image.png

创建自定义列与列表条目

“Format-Table”与“Format-List”都能使用哈希表结构,创建自定义列或自定义表条目。

可以通过提供与属性名称不同的列头创建自定义列:
image.png
甚至使用更复杂的数学表达式:

  1. PS C:\> Get-Process | Format-Table Name,
  2. >> @{name='VM(MB)'; expre={$_.VM / 1MB -as [int]}} -AutoSize

image.png
我们使用一个特殊的哈希表,创建一个显示为“VM(MB)”的自定义列。

  • 这是以分号作为分隔符的第一部分,第二部分定义了值或表达式,示例表示将VM属性的值除以1MB。
  • 在PowerShell中的斜线是除法操作。
  • PowerShell能够识别“KB”“MB”“GB”“TB”和“PB”这些缩写,分别代表kilobyte、megabyte、gigabyte、terabyte和petabyte。

除法运算的结果会带有小数点,“-as [int]”操作符可以将数据结果从浮点型转换成整型。该Shell会根据情况向上或者向下取整,以便显示合适的结果。

与“Select-Object”不同,它的哈希表仅接受一个Name和Expression作为哈希键(虽然对于Name属性,可以用N、L和Label;对于Expression属性,可以用E)。
“Format-”命令可以处理用于控制显示的额外的键字,对于“Format-Table”尤其有效。

  • FormatString:指定一个格式化代码,使得数据按指定格式显示。该参数主要用于数值型和日期型数据。
    • 可以到MSDN的“Formatting Types”页中,查看用于标准数值与日期格式的格式化代码,以及用于自定义数值与日期格式的格式化代码。
  • Width:指定列宽。
  • Alignment:指定列的对齐格式,可以为左对齐或者右对齐。

使用额外的键修改上面的代码,实现同样的输出结果,并更加美观。

  1. PS C:\> Get-Process | Format-Table Name,
  2. >> @{name='VM(MB)'; expre={$_.VM}; formatstring='F2'; align='right'} -AutoSize

image.png

输出结果

输出到主机

如果命令最后是Format-开头的Cmdlet,由Format-开头的Cmdlet创建的格式化指令将会传递给“Out-Default”,然后该命令会将结果传递给“Out-Host”,最后显示结果到显示屏。
image.png
上面的语句,实际上是运行了下面的语句:
image.png

输出到打印机或文件

另外一种方式是用管道把格式化指令传递给“Out-File”或“Out-Printer”,从而将结果输出到文件或者打印机中。只有这三个以“Out-”开头的Cmdlet才可以跟在以“Format-”开头的Cmdlet后面。

请记住,“Out-Printer”和“Out-File”都有默认的输出宽度,意味着输出结果在文件或打印的结果看上去可能和显示器上看到的不一致。但这些Cmdlets允许使用“-width”参数控制宽度。

输出到GridView

“Out-GridView”提供了另一种形式的输出结果。但是,从技术角度来讲,这并不真正意义上的格式化。

实际上,“Out-GridView”完全绕过了格式化子系统。它不需要调用以“Format-”开头的Cmdlets,不生成格式化指令,不会在控制台窗口输出文本结果。“Out-GridView”不接收“Format-”Cmdlet的输出,仅接收其他Cmdlets输出的对象。
image.png

常见误区

总是在最右边格式化

切记:**format right**。以“Format-”开头的Cmdlet应该是命令行最后一个cmdlet。而Out-File与Out-Printer却是例外。其原因是以“Format-”开头的Cmdlets生成格式化指令,仅有“Out-”Cmdlet能合理地处理这些指令。
image.png
通过“Gm”,并没有显示关于服务对象的信息,因为“Format-Table”并不输出服务对象。它消费你通过管道传输过来的服务对象,并且输出格式化指令。

再次提醒:“Out-File”和“Out-Printer”是仅有的需要跟在以“Format-”开头的Cmdlet后面的命令。

一次一个对象

另外一件需要避免的事就是把多种对象放入管道。格式化系统先在管道中查找第一个对象,然后使用定义格式处理该对象。如果管道包含两个或以上的对象,那么结果可能与你期望的会有不同。
比如运行:
image.png
其中分号允许我们把两个命令合并在一个命令行中,而不是把第一个命令的输出并以管道形式传入第二个命令。这意味着两个命令单独运行,但是会把它们的输出结果传到相同的管道中。
image.png
会看到第一个命令的输出是合理的,但是当显示服务对象时,输出结果会使用列表显示,而不是使用相同的表格。
PowerShell的格式化系统,并不是被设计用于接收不同类型的对象。

如果你希望将来自不同地方的两个信息以同一种格式输出,那该怎么办?你当然可以这么做,格式化系统能够以非常优雅的方式实现这点。技术上而言,格式化系统可以处理多种类型的对象。

运行“Dir | Gm”,可以发现管道包含了“DirectoryInfo”和“FileInfo”对象(Gm可以与包含不同类型对象的管道结合使用,并显示所有对象的成员信息)。当仅运行Dir时,输出结果非常清晰。这是因为微软已经对“DirectoryInfo”和“FileInfo”对象提供了预定义的自定义格式化实体,该格式化由“Format-Custom”完成。
Format-Custom”主要用于展示多种预定义视图。技术上,你可以自己创建预定义视图,但是所需的XML语法相对复杂,并且目前没有文档支持,所以当前仅能使用微软提供的定制视图。例如,PowerShell的帮助系统以对象形式储存,你所看到已格式化的帮助文件是将这些对象传递给自定义视图处理后的结果。