Powershell一個最吸引人的功能是它能夠將任何對象轉換成文本,我們已經使用過將對象屬性以不同的版式轉換成文本,并且輸出。更令人驚奇的是Powershell會把最重要最能代表這個對象本質的信息輸出。一個對象有很多屬性,為什么它單單就輸出那幾個屬性呢?
如果使用:
Powershell會最大限度的輸出每個屬性,但是這樣的輸出基本上沒有意義,不利于用戶閱讀。那到底是什么讓Powershell默認只顯示此屬性不顯示彼屬性呢?是“擴展類型系統”Extended Type System (ETS),ETS會對管道中對象轉換成文本的機制進行宏觀調控。
ETS由兩部分組成,一部分控制對象的版式,一部分控制對象的屬性,今天主要關心第一部分。
文本轉換不可逆
在管道中將對象結果轉換成文本后,不能再將文本轉換成對象,因為ETS不能處理文本。
如果通過ConvertTo-String將目錄列表的轉換成String后,使用Format-Table和Format-List這些命令就會無效。
目錄: C:Powershell
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2011/12/19 17:05 ABC
d---- 2011/12/19 17:06 ABD
d---- 2011/12/19 17:06 ABE
PS C:Powershell> $text | Format-Table
目錄: C:Powershell
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2011/12/19 17:05 ABC
d---- 2011/12/19 17:06 ABD
d---- 2011/12/19 17:06 ABE
PS C:Powershell> $text | Format-List
目錄: C:Powershell
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2011/12/19 17:05 ABC
d---- 2011/12/19 17:06 ABD
d---- 2011/12/19 17:06 ABE
選擇屬性
在顯示對象結果時如果使用了像Format-Table這樣的命令,ETS也不會起作用,因為Format-Table將每個屬性的值轉換成了文本。所以有的時候,顯示那些屬性最好自己指定清楚,不要把生殺大權交給ETS。
Mode FullName
---- --------
d---- C:PowershellABC
d---- C:PowershellABD
d---- C:PowershellABE
d---- C:Powershellmyscript
-a--- C:Powershella.ccs
-a--- C:Powershella.csv
-a--- C:Powershella.html
-a--- C:Powershella.txt
-a--- C:Powershellalias
已知對象格式化
如果使用了格式化的命令,但是沒有指定具體的屬性(如: dir | Format-Table)。ETS將會首次大展拳腳,它會決定那些對象應當顯示,那些屬性應當被自動選擇。ETS在做這些工作之前,首先應當弄清楚,那些對象能夠被轉換成文本。
Dir 返回一個System.IO.DirectoryInfo對象,并且包含了這個對象里面的System.IO.FileInfo對象和System.IO.DirectoryInfo子對象。這樣ETS就可以去檢查自己的內部記錄,通過內部記錄的配置,將對象轉換成文本。這些內部記錄為XML文件,擴展名為“.ps1xml”
目錄: C:WindowsSystem32WindowsPowerShellv1.0
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2009/6/11 5:24 27338 Certificate.format.ps1xml
-a--- 2009/6/11 5:24 27106 Diagnostics.Format.ps1xml
-a--- 2009/6/11 5:24 72654 DotNetTypes.format.ps1xml
-a--- 2009/6/11 5:24 24857 FileSystem.format.ps1xml
-a--- 2009/6/11 5:24 257847 Help.format.ps1xml
-a--- 2009/6/11 5:24 89703 PowerShellCore.format.ps1xml
-a--- 2009/6/11 5:24 18612 PowerShellTrace.format.ps1xml
-a--- 2009/6/11 5:24 20120 Registry.format.ps1xml
-a--- 2009/6/11 5:24 24498 WSMan.Format.ps1xml
每一個對象詳細地被定義在這些XML文件中,定義包括那些對象屬性支持轉換成文本,那些對象應當默認顯示在列表或者表格中。
有一點之前說過,對于一行上面的混合命令“ Get-Process ; dir”ETS不支持,要想避免最好的方式是每個命令明確地指定版式。
PS C:Powershell> Get-Process | Format-Table ; dir | Format-Table
未知對象格式化
在ps1xml中定義過的對象屬于已知對象,那些未知對象ETS應當怎樣處理呢?對于未知對象,ETS遵循一個規律:
如果對象的屬性少于5個則表格顯示,否則列表顯示。
下面的例子創建一個對象,并向對象中逐個增加屬性。
A
-
1
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "B" -Value "2" -InputObject $obj
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "C" -Value "3" -InputObject $obj
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "D" -Value "4" -InputObject $obj
PS C:Powershell> $obj
A B C D
- - - -
1 2 3 4
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "E" -Value "5" -InputObject $obj
PS C:Powershell> $obj
A : 1
B : 2
C : 3
D : 4
E : 5
應急模式
如果ETS從輸出中發現臨界狀態,會自動切換到列表顯示。例如“Get-Process; Dir”,ETS正在以表格形式輸出Process對象,但是突然碰到一個FileInfo對象,就會直接切換到列表模式,輸出其它類型的對象。
隱藏列
如果碰到未知的對象,ETS會試著從管道輸出的第一個結果尋找線索,這樣可能導致一個奇怪的現象。ETS會根據未知對象的第一個結果,來判斷屬性,但第一條結果的屬性并不總會輸出。可能再碰到包含更多屬性的對象時,當前選擇的屬性信息就可能會被抑制。
接下來的例子演示那些信息會被抑制,Get-Process 返回正在運行的所有進程,然后通過StartTime進行排序,最輸出每個進程的名稱和開啟時間:
當執行上面的命令行時,會收到許多錯誤信息。這些錯誤信息并不是來源于命令,而是可能因為當前控制臺沒有管理員權限,某些系統進程拒絕訪問。輸出的進程中可能有一部分進程只有進程名(Name),沒有開啟時間(StartTime),開啟時間被抑制了。
使用Select-Object,會刪除對象的某些屬性,但是對象本身的屬性是不能刪除的,所以ETS會在管道中重新生成一個對象,類型為:System.Management.Automation.PSCustomObject。
因為PSCustomObject在ETS配置中沒有記錄,就會輸出全部屬性。管道結果之前根據StartTime升序排列過,所以前面的進程由于權限問題沒有StartTime。
擴充ETS
ETS配置中包含的類型對象會以最佳的方式轉換成文本。但是對于未知對象就表現不完美了,表現不完美并不代表束手無策。幸運的是可以通過擴充ETS讓ETS以最佳的方式處理新對象。
擴充ETS的第一步是確定待擴充對象類型。我們可能經常通過Get-WmiObject 來獲取WMI服務。但是不太喜歡Powershell對于它的默認輸出,就可以擴充ETS了。
__GENUS : 2
__CLASS : Win32_Processor
__SUPERCLASS : CIM_Processor
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Processor.DeviceID="CPU0"
__PROPERTY_COUNT : 48
__DERIVATION : {CIM_Processor, CIM_LogicalDevice, CIM_LogicalEle
首先確定命令返回結果的對象類型
發現目標類型為:System.Management.ManagementObject
接下來創建一個配置文件:
將文件保存為Win32_Processor.format.ps1xml,然后使用命令Update-FormatData把它加載進ETS,會立即生效
Name Description ID
---- ----------- --
CPU0 x64 Family 6 Model 15 Stepp... BFEBFBFF000006FD
但是這樣的定義可能有個缺點,當我們獲取其它WMI對象時,也會根據我們定義的規則顯示。
Name Description ID
---- ----------- --
Remote Admin
Default share
HP LaserJet P2050 Series PCL6
Remote IPC
Printer Drivers
出現上面的情況,是因為WMI的所有對象都會以System.Management.ManagementObject類型返回。因此ETS沒有出錯,罪魁禍首是WMI這個特殊的類型。所以擴充ETS時一定要細化一個具體的類型。事實上WMI對象有一個PSTypeNames屬性,通過它就可以找到更具體的類型。
上面顯示了WMI對象類型的繼承層次。所以我們需求中要擴展的對象類型應該為:System.Management.ManagementObject#rootcimv2Win32_Processor
所以應當修改配置文件,重新加載更新。更新時會有一條異常
Update-FormatData : 加載格式數據文件時出錯:
Microsoft.PowerShell,C:PowershellWin32_Processor.format.ps1xml: 文件被跳過,
因為該文件已在“Microsoft.PowerShell”中出現過。
異常可以忽略,然后重新測試。
Name Description ID
---- ----------- --
CPU0 x64 Family 6 Model 15 Stepp... BFEBFBFF000006FD
PS C:Powershell> Get-WmiObject win32_share
Name Path Description
---- ---- -----------
ADMIN$ C:Windows Remote Admin
C$ C: Default share
這樣ETS的擴充只對Win32_Processor有效了。不會影響到其他父類型對象。
新聞熱點
疑難解答
圖片精選