1. 程式人生 > >Powershell使用真實的對象工作

Powershell使用真實的對象工作

調用 rem 單單 remote 影響 proc ctu directory 保護

Powershell使用真實的對象工作

來源 https://www.pstips.net/powershell-work-with-reallife-objects.html

每一個Powershell命令都會返回一個對象,但是返回的對象不易操作,因為控制臺解釋器會自動將對象轉換成可視的文本,這樣就會丟失絕大多數對象的信息。

在變量中存儲結果

不要將結果在控制臺輸出可以防止對象轉換成文本。控制臺是一個不安全的地方,任何對象輸出後都會自動轉換成文本,最安全的方式是將對象保存在變量中。如果想將對象輸出為文本,可以在控制臺輸入變量名。

PS C:Powershell> $FileList=dir
PS C:Powershell> $FileList

    目錄: C:Powershell

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2011/12/19     17:43       8956 a.ccs
-a---        2011/12/19     18:02      46411 a.csv

事實上述存儲在$FileList變量中的並不是真實的對象,而是一個對象數組,數組可以通過索引訪問得到真實的對象。

PS C:Powershell> $obj=(dir)[0]
PS C:Powershell> $obj

    目錄: C:Powershell

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2011/12/19     17:43       8956 a.ccs

使用對象的屬性

可以使用Get-Member得到一個對象所有的屬性:

PS C:Powershell> $obj=(dir)[0]
PS C:Powershell> $obj | Get-Member -MemberType Property

   TypeName: System.IO.FileInfo

Name              MemberType Definition
----              ---------- ----------
Attributes        Property   System.IO.FileAttributes Attributes {get;set;}
CreationTime      Property   System.DateTime CreationTime {get;set;}
CreationTimeUtc   Property   System.DateTime CreationTimeUtc {get;set;}
Directory         Property   System.IO.DirectoryInfo Directory {get;}
DirectoryName     Property   System.String DirectoryName {get;}
Exists            Property   System.Boolean Exists {get;}
Extension         Property   System.String Extension {get;}
FullName          Property   System.String FullName {get;}
IsReadOnly        Property   System.Boolean IsReadOnly {get;set;}
LastAccessTime    Property   System.DateTime LastAccessTime {get;set;}
LastAccessTimeUtc Property   System.DateTime LastAccessTimeUtc {get;set;}
LastWriteTime     Property   System.DateTime LastWriteTime {get;set;}
LastWriteTimeUtc  Property   System.DateTime LastWriteTimeUtc {get;set;}
Length            Property   System.Int64 Length {get;}
Name              Property   System.String Name {get;}

如果屬性的定義列中包含{get;set}表明該屬性可以被更新:

PS C:Powershell> $obj.LastAccessTime

2011年12月19日 17:43:37

PS C:Powershell> $obj.LastAccessTime=Get-Date
PS C:Powershell> $obj.LastAccessTime

2012年1月11日 14:21:01

Powershell特殊屬性

Powershell中 可以給一個對象增加屬性,增加的屬性仍然可以通過Get-Member的標簽辨別,因為對象的正常屬性標簽名為:Property,新增加的屬性標簽多了一個前綴,如ScriptProperty和NoteProperty。
一個NoteProperty包含了靜態的數據。一個ScriptProperty中包含了一段腳本,通過腳本計算出屬性的值。
下面的例子新建一個對象$obj,給$obj增加兩個屬性一個為NoteProperty,一個為ScriptProperty,輸出$obj ,CurrentTime屬性會自動更新,AddTime則不會。

PS C:Powershell> $obj=New-Object PSobject
PS C:Powershell> $obj | Add-Member -MemberType NoteProperty -Name AddTime -Value (get-date)
PS C:Powershell> $obj | Add-Member -MemberType ScriptProperty -Name CurrentTime -Value {get-date}
PS C:Powershell> $obj

AddTime                                                     CurrentTime
-------                                                     -----------
2012/1/11 14:35:38                                          2012/1/11 14:36:35

PS C:Powershell> $obj

AddTime                                                     CurrentTime
-------                                                     -----------
2012/1/11 14:35:38                                          2012/1/11 14:36:44

MemberType包括:
AliasProperty:另外一個屬性的別名
CodeProperty:通過靜態的.Net方法返回屬性的內容
Property:真正的屬性
NoteProperty:隨後增加的屬性
ScriptProperty:通過腳本執行返回一個屬性的值
ParameterizedProperty:需要傳遞參數的屬性

調用對象的方法

同樣可以通過Get-Memeber獲得一個對象支持的所有方法:

PS C:Powershell> $obj= (dir)[0]
PS C:Powershell> $obj | Get-Member -me method

   TypeName: System.IO.FileInfo

Name                      MemberType Definition
----                      ---------- ----------
AppendText                Method     System.IO.StreamWriter AppendText()
CopyTo                    Method     System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(strin...
Create                    Method     System.IO.FileStream Create()
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateText                Method     System.IO.StreamWriter CreateText()
Decrypt                   Method     System.Void Decrypt()
Delete                    Method     System.Void Delete()
Encrypt                   Method     System.Void Encrypt()
Equals                    Method     bool Equals(System.Object obj)
GetAccessControl          Method     System.Security.AccessControl.FileSecurity GetAccessControl(), System.Security....
GetHashCode               Method     int GetHashCode()
GetLifetimeService        Method     System.Object GetLifetimeService()
GetObjectData             Method     System.Void GetObjectData(System.Runtime.Serialization.SerializationInfo info, ...
GetType                   Method     type GetType()
InitializeLifetimeService Method     System.Object InitializeLifetimeService()
MoveTo                    Method     System.Void MoveTo(string destFileName)
Open                      Method     System.IO.FileStream Open(System.IO.FileMode mode), System.IO.FileStream Open(S...
OpenRead                  Method     System.IO.FileStream OpenRead()
OpenText                  Method     System.IO.StreamReader OpenText()
OpenWrite                 Method     System.IO.FileStream OpenWrite()
Refresh                   Method     System.Void Refresh()
Replace                   Method     System.IO.FileInfo Replace(string destinationFileName, string destinationBackup...
SetAccessControl          Method     System.Void SetAccessControl(System.Security.AccessControl.FileSecurity fileSec...
ToString                  Method     string ToString()

調用一個對象的方法時,省略括號可以獲取一個方法的詳細定義信息:

PS C:Powershell> $obj.CreationTime

2011年12月19日 17:43:37

PS C:Powershell> $obj.MoveTo

MemberType          : Method
OverloadDefinitions : {System.Void MoveTo(string destFileName)}
TypeNameOfValue     : System.Management.Automation.PSMethod
Value               : System.Void MoveTo(string destFileName)
Name                : MoveTo
IsInstance          : True

調用對象的Delete方法:或者使用 Remove-Variable cmdlet。

PS C:Powershell> Test-Path $obj
True
PS C:Powershell> $obj.Delete()
PS C:Powershell> Test-Path $obj
False

不同的方法類型

類似於屬性,Powershell對象也可以增加方法,方法類型包括:
CodeMethod:映射到靜態的.NET方法
Method:正常的方法
ScriptMethod:一個執行Powershell腳本的方法

Powershell變量的作用域

https://www.pstips.net/powershell-scope-of-variables.html

Powershell所有的變量都有一個決定變量是否可用的作用域。Powershell支持四個作用域:全局、當前、私有和腳本。有了這些作用域就可以限制變量的可見性了,尤其是在函數和腳本中。

如果我們對變量不做特別的聲明,Powershell解釋器會自動處理和限制變量的作用域。將下面的內容命令保存著至test1.ps1
$windows = $env:windir
“Windows Folder: $windows”

然後在控制臺給變量$windows賦值,並調用Test.ps1腳本。

PS> $windows="Hellow"
PS> .\test.ps1
Windows Folder: C:\Windows
PS> $windows
Hellow

調用腳本時,會分配一個變量$windows,在腳本調用結束後,這個變量被回收,腳本中的變量不會影響腳本外的變量,因為它們在不同的作用域中。powershell會針對每個函數和腳本給它們分配不同的作用域。

更改變量的可見性

你可以很容易的看到沒有Powershell解釋器自動限制可見性時會發生什麽狀況,同樣是剛才的腳本,剛才的命令,只是在運行腳本時多加上一個點”.” 和一個空格:

PS> $windows="Hellow"
PS> . .\test.ps1
Windows Folder: C:\Windows
PS> $windows
C:Windows

在運行腳本時使用一個原點和空格,Powershell解釋器就不會為腳本本身創建自己的變量作用域,它會共享當前控制臺的作用域,這種不太靈活但卻簡單的方法,使用時一定要格外小心。

加強變量可見性限制的優點:清空初始化環境
可以假設一個場景,如果你在當前控制臺不小心定義了一個只讀的常量,這個常量既不能更新也不能刪除,很是麻煩。但是如果你在腳本中操作這個變量就不成問題,因為腳本有自己的作用域。例如,將下面文本保存為test.ps1,並調用沒有任何問題:

New-Variable a -value 1 -option Constant
"Value: $a"
PS> .\test.ps1
Value: 1
PS> .\test.ps1
Value: 1

但是如果你通過圓點禁用作用域限制,調用test.ps1,就會有異常,因為一個常量不能被創建兩次。

PS> . .\test.ps1
Value: 1
PS> . .\test.ps1
New-Variable : A variable with name ‘a‘ already exists.
Attest.ps1:1 char:13
+ New-Variable <<<<  a -value 1 -option Constant
    + CategoryInfo          : ResourceExists: (a:String) [New-Variable], SessionStateException
    + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand

所以這種變量的作用域限制可以把變量的沖突降到最小。

設置單個變量的作用域

到目前為止,看到的變量作用域的改變都是全局的,能不能針對某個具體變量的作用域做一些個性化的設置。

$global
全局變量,在所有的作用域中有效,如果你在腳本或者函數中設置了全局變量,即使腳本和函數都運行結束,這個變量也任然有效。

$script
腳本變量,只會在腳本內部有效,包括腳本中的函數,一旦腳本運行結束,這個變量就會被回收。

$private
私有變量,只會在當前作用域有效,不能貫穿到其他作用域。

$local
默認變量,可以省略修飾符,在當前作用域有效,其它作用域只對它有只讀權限。

打開Powershell控制臺後,Powershell會自動生成一個新的全局作用域。如果增加了函數和腳本,或者特殊的定義,才會生成其它作用域。在當前控制臺,只存在一個作用域,通過修飾符訪問,其實訪問的是同一個變量:

PS> $logo="www.pstips.net"
PS> $logo
www.pstips.net
PS> $private:logo
www.pstips.net
PS> $script:logo
www.pstips.net
PS> $private:logo
www.pstips.net
PS> $global:logo
www.pstips.net

當調用一個已定義的函數,Powershell會生成第二個作用域,它可以對調用者的作用域中的變量執行讀操作,但是不能執行寫操作。

PS> function f(){ "var=$var";$var="function inner";$var }
PS> $var="I am in console."
PS> $var
I am in console.
PS> f
var=I am in console.
function inner
PS> $var
I am in console.

怎樣把當前控制臺中的變量保護起來,不讓它在函數和腳本中被訪問,Private修飾符就派上了用場。

PS>  function f(){ "var=$var";$var="function inner";$var }
PS> $private:var="i am a private variable in console,other scope can not access me."
PS> f
var=
function inner
PS> $private:var
i am a private variable in console,other scope can not access me.

對於$private限制的變量能不能在函數中通過$global修改呢?不但不能修改,還會刪除當前的$private變量

PS> Function f(){ "var=$var";$global:var=" Try to change variable in function"}
PS> $private:var="I am a private variable"
PS> $private:var
I am a private variable
PS> $var
I am a private variable
PS> f
var=
PS> $private:var
PS> $var
PS>
PS> $private -eq $null
True

但是$local 修飾的變量則可以通過$global在函數內部更改。

PS> Function f(){ "var=$var";$global:var=" Try to change variable in function"}
PS> $var="I am a local variable."
PS> $var
I am a local variable.
PS> $private:var
I am a local variable.
PS> f
var=I am a local variable.
PS> $var
 Try to change variable in function
PS> $local:var
 Try to change variable in function

Powershell對象=屬性+方法

https://www.pstips.net/powershell-object-contains-properties-and-methods.html

在現實世界中,你可能已經了解對象就是那些能夠摸到的東西。Powershell中的對象和現實生活很相似。例如要在現實生活中描述一把小刀。我們可能會分兩方面描述它
屬性:一把小刀擁有一些特殊的屬性,比如它的顏色、制造商、大小、刀片數。這個對象是紅色的,重55克,有3個刀片,ABC公司生產的。因此屬性描述了一個對象是什麽。
方法:可以使用這個對象做什麽,比如切東西、當螺絲釘用、開啤酒蓋。一個對象能幹什麽就屬於這個對象的方法。

創建對象

通過New-Object可以創建一個對象,甚至可以創建一個虛擬的小刀,但是第一步需要創建一個空對象。空對象什麽都沒有,如果調用它,不會返回任何東西。

PS C:Powershell> $pocketknife=New-Object object
PS C:Powershell> $pocketknife
System.Object

增加屬性

接下來描述這個對象是什麽

PS C:Powershell> Add-Member -InputObject $pocketknife -Name Color -Value "Red"
-MemberType NoteProperty
PS C:Powershell> $pocketknife

Color
-----
Red

PS C:Powershell> Add-Member -InputObject $pocketknife -Name Weight -Value "55"
-MemberType NoteProperty
PS C:Powershell> $pocketknife | Add-Member NoteProperty Blades 3
PS C:Powershell> $pocketknife | Add-Member NoteProperty Manufacturer ABC
PS C:Powershell> $pocketknife

Color Weight Blades Manufacturer
----- ------ ------ ------------
Red   55          3 ABC

增加方法

給一個對象增加了屬性後,這個對象就有形狀了,但是它仍然不能做任何事,要想它做事,必須給它增加方法。同樣使用Add-Member,不過-memberType 選項使用ScriptMethod。

1 2 3 4 5 6 7 # 增加一個新方法: Add-Member -memberType ScriptMethod -In $pocketknife ` -name cut -Value { "I‘m whittling now" } # 指定參數類型增加一個新方法: Add-Member -in $pocketknife ScriptMethod screw { "Phew...it‘s in!" } #直接通過管道增加一個新方法: $pocketknife | Add-Member ScriptMethod corkscrew { "Pop! Cheers!" }

方法添加成功後就可以調用了

PS C:Powershell> $pocketknife.cut()
I‘m whittling now
PS C:Powershell> $pocketknife.screw()
Phew...it‘s in!
PS C:Powershell> $pocketknife.corkscrew()
Pop! Cheers!

在調用方法時如果沒有使用圓括號,方法不會執行,但是可以返回方法的基本信息。

PS C:Powershell> $pocketknife.corkscrew

Script                      :  "Pop! Cheers!"
OverloadDefinitions : {System.Object corkscrew();}
MemberType           : ScriptMethod
TypeNameOfValue  : System.Object
Value                      : System.Object corkscrew();
Name                     : corkscrew
IsInstance               : True

到目前為止一個虛擬的小刀對象就創建完成了,一個對象包含數據(屬性)和動作(方法)。

Powershell變量的類型和強類型

http://www.pstips.net/powershell-variable-strongly-typing.html

變量可以自動存儲任何Powershell能夠識別的類型信息,可以通過$variable的GetType().Name查看和驗證Powershell分配給變量的數據類型。

PS> (10).gettype().name
Int32
PS> (9999999999999999).gettype().name
Int64
PS> (3.14).gettype().name
Double
PS> (3.14d).gettype().name
Decimal
PS> ("WWW.MOSSFLY.COM").gettype().name
String
PS> (Get-Date).gettype().name
DateTime

Powershell會給數據分配一個最佳的數據類型;如果一個整數超出了32位整數的上限([int32]::MaxValue),它就會分配一個64位整數的數據類型;如果碰到小數,會分配一個Double類型;如果是文本,Powershell會分配一個String類型;如果是日期或者時間,會被存儲為一個Datetime對象。
這種類型自適應也稱作“弱類型”,雖然使用起來方便,但是也會有一些限制,甚至危險。如果powershell選擇了一個錯誤的類型付給變量,可能會引發一些奇怪的現象。例如有一個變量要存儲的是即將拷貝文件的個數,可是在賦值時付了一個字符串,Powershell不會去做過多的判斷,它會更新這個變量的類型,並且存儲新的數據。所以一般專業的程序員或者腳本開發者更喜歡使用“強類型”,哪怕在賦值時類型不兼容的報錯,他們也樂意接受。
喜歡使用強類型的另一個原因是:每一個數據類型都有屬於自己的函數。例如DateTime,和XML,盡管這兩種類型都可以用純文本表示,但是使用強類型[DateTime]和[XML],對於數據操作起來更方便,這兩個類型的方法可是很豐富奧!

指定類型定義變量

定義變量時可以在變量前的中括號中加入數據類型。例如定義一個Byte類型的變量,因為Byte的定義域為[0,255],一旦嘗試使用一個不在定義域中的值賦給該變量就會顯示一條錯誤信息。

PS> [byte]$b=101
PS> $b
101
PS> $b=255
PS> $b
255
PS> $b.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Byte                                     System.ValueType

PS> $b=256

Cannot convert value "256" to type "System.Byte". Error: "Value was either too large or too small for an unsigned byte.
"
At line:1 char:3
+ $b <<<< =256
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

使用固定類型的優點

手動地定義類型的一個重要原因是每個特殊的數據類型都有自己的特殊命令和特殊方法。比如把一個日期字符串賦給一個變量,Powershell不會自動把這個字符串轉換成日期對象賦給一個變量,因為Powershell畢竟是機器,沒有人那麽智能。當你在賦值時指定DateTime類型時,你會發現幾乎所有的.Net 中DateTime類型的方法在這裏都得到支持。

PS> [DateTime]$date="2012-12-20 12:45:00"
PS> $date

2012年12月20日 12:45:00

PS> $date.DayOfWeek
Thursday
PS> $date.DayOfYear
355
PS> $date.AddDays(-10)

2012年12月10日 12:45:00

Powershell處理Xml文檔也很方便,
例如有如下LogoTest.xml

1 2 3 4 5 6 7 8 9 10 <logotest> <extensions> <e>.exe</e> <e>.dll</e> </extensions> <files> <f></f> </files> <dirs></dirs> </logotest>

查詢.exe 和 .dll結點

PS> [ XML ]$xml=(Get-Content .LogoTestConfig.xml)
PS> $xml.LogoTest.Extensions.E
.exe
.dll

Powershell 默認支持的.NET類型如下。
[array],[bool],[byte],[char],[datetime],[decimal],[double],[guid],[hashtable],[int16],[int32],[int],[int64],[long],[nullable],[psobject],[regex],[sbyte].[scriptblock],[single],[float],[string],[switch],[timespan],[type],[uint16],[uint32],[uint64],[ XML ]

Powershell擴展類型系統

https://www.pstips.net/powershell-extended-type-system-1.html

Powershell一個最吸引人的功能是它能夠將任何對象轉換成文本,我們已經使用過將對象屬性以不同的版式轉換成文本,並且輸出。更令人驚奇的是Powershell會把最重要最能代表這個對象本質的信息輸出。一個對象有很多屬性,為什麽它單單就輸出那幾個屬性呢?
如果使用:

Dir | Format-Table * -wrap
PSP PSP PSC PSD PSP PSI Bas Mod Nam Par Exi Roo Ful Ext Cre Cre Las La La La At
ath are hil riv rov sCo eNa e   e   ent sts t   lNa ens ati ati tAc st st st tr
    ntP dNa e   ide nta me                      me  ion onT onT ces Ac Wr Wr ib
    ath me      r   ine                                 ime ime sTi ce it it ut
                      r                                     Utc me  ss eT eT es
                                                                    Ti im im
                                                                    me e  eU
                                                                    Ut    tc
                                                                    c
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- -- -- -- --
Mic Mic ABC C   Mic Tru ABC d-- ABC Pow Tru C: C:     201 201 201 20 20 20 Di
ros ros         ros   e     --      ers   e     Pow     1/1 1/1 1/1 11 11 11 re
oft oft         oft                 hel         ers     2/1 2/1 2/1 /1 /1 /1 ct
.Po .Po         .Po                 l           hel     9 1 9 9 9 1 2/ 2/ 2/ or
wer wer         wer                             lA     7:0 :05 7:0 19 19 19  y
She She         She                             BC      5:5 :55 5:5  9  1  9
ll. ll.         ll.                                     5       5   :0 7: :0
Cor Cor         Cor                                                 5: 05 5:
eF eF         eF                                                 55 :5 55
ile ile         ile                                                    5
Sys Sys         Sys
tem tem         tem
::C ::C
?? ??
owe owe
rsh rsh
ell ell
AB
C

Powershell會最大限度的輸出每個屬性,但是這樣的輸出基本上沒有意義,不利於用戶閱讀。那到底是什麽讓Powershell默認只顯示此屬性不顯示彼屬性呢?是“擴展類型系統”Extended Type System (ETS),ETS會對管道中對象轉換成文本的機制進行宏觀調控。
ETS由兩部分組成,一部分控制對象的版式,一部分控制對象的屬性,今天主要關心第一部分。

文本轉換不可逆

在管道中將對象結果轉換成文本後,不能再將文本轉換成對象,因為ETS不能處理文本。
如果通過ConvertTo-String將目錄列表的轉換成String後,使用Format-Table和Format-List這些命令就會無效。

PS C:Powershell> $text= dir | Out-String
PS C:Powershell> $text

    目錄: 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。

PS C:Powershell> dir | Format-Table Mode,FullName

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在做這些工作之前,首先應當弄清楚,那些對象能夠被轉換成文本。

PS C:Powershell> (dir)[0].GetType().FullName
System.IO.DirectoryInfo

Dir 返回一個System.IO.DirectoryInfo對象,並且包含了這個對象裏面的System.IO.FileInfo對象和System.IO.DirectoryInfo子對象。這樣ETS就可以去檢查自己的內部記錄,通過內部記錄的配置,將對象轉換成文本。這些內部記錄為XML文件,擴展名為“.ps1xml”

PS C:Powershell> dir $PSHOME *format.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個則表格顯示,否則列表顯示。
下面的例子創建一個對象,並向對象中逐個增加屬性。

PS C:Powershell> $obj=New-Object PSObject
PS C:Powershell> Add-Member -Name A -Value 1 -InputObject $obj
MemberType: PS C:Powershell>
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "A" -Value "1" -InputObject $obj
PS C:Powershell> $obj

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進行排序,最輸出每個進程的名稱和開啟時間:

PS C:Windowssystem32> Get-Process | Sort-Object StartTime | Select-Object Name
,StartTime

Sort-Object : 獲取“StartTime”時發生異常:“拒絕訪問。”
所在位置 行:1 字符: 26
+ Get-Process | Sort-Object <<<< StartTime | Select-Object Name,StartTime
+ CategoryInfo : InvalidResult: (System.Diagnostics.Process (audi
odg):PSObject) [Sort-Object], GetValueInvocationException
+ FullyQualifiedErrorId : ExpressionEvaluation,Microsoft.PowerShell.Comman
ds.SortObjectCommand
當執行上面的命令行時,會收到許多錯誤信息。這些錯誤信息並不是來源於命令,而是可能因為當前控制臺沒有管理員權限,某些系統進程拒絕訪問。輸出的進程中可能有一部分進程只有進程名(Name),沒有開啟時間(StartTime),開啟時間被抑制了。
使用Select-Object,會刪除對象的某些屬性,但是對象本身的屬性是不能刪除的,所以ETS會在管道中重新生成一個對象,類型為:System.Management.Automation.PSCustomObject。

PS C:Powershell> Get-Process | foreach {$_.gettype().fullname} | select -f 1
System.Diagnostics.Process
PS C:Powershell> (Get-Process | foreach {$_.gettype().fullname} | select -f 1 Name ).getType().fullname
System.Management.Automation.PSCustomObject

因為PSCustomObject在ETS配置中沒有記錄,就會輸出全部屬性。管道結果之前根據StartTime升序排列過,所以前面的進程由於權限問題沒有StartTime。

擴充ETS

ETS配置中包含的類型對象會以最佳的方式轉換成文本。但是對於未知對象就表現不完美了,表現不完美並不代表束手無策。幸運的是可以通過擴充ETS讓ETS以最佳的方式處理新對象。
擴充ETS的第一步是確定待擴充對象類型。我們可能經常通過Get-WmiObject 來獲取WMI服務。但是不太喜歡Powershell對於它的默認輸出,就可以擴充ETS了。

PS C:Powershell> Get-WmiObject Win32_Processor

__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

首先確定命令返回結果的對象類型

PS C:Powershell> $object = Get-WmiObject Win32_Processor | Select-Object -first 1
PS C:Powershell> $object.GetType().FullName
System.Management.ManagementObject

發現目標類型為:System.Management.ManagementObject
接下來創建一個配置文件:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 CustomView System.Management.ManagementObject <label>Name</label> 12 <label>Description</label> 30 <label>ID</label> DeviceID Description ProcessorID

將文件保存為Win32_Processor.format.ps1xml,然後使用命令Update-FormatData把它加載進ETS,會立即生效

PS C:Powershell> Update-FormatData .Win32_Processor.format.ps1xml
PS C:Powershell> Get-WmiObject win32_processor

Name         Description                    ID
----         -----------                    --
CPU0         x64 Family 6 Model 15 Stepp... BFEBFBFF000006FD

但是這樣的定義可能有個缺點,當我們獲取其它WMI對象時,也會根據我們定義的規則顯示。

PS C:Powershell> Get-WmiObject Win32_Share

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屬性,通過它就可以找到更具體的類型。

PS C:Powershell> $object = Get-WmiObject Win32_Processor | Select-Object -first1
PS C:Powershell> $object.PSTypeNames
System.Management.ManagementObject#rootcimv2Win32_Processor
System.Management.ManagementObject
System.Management.ManagementBaseObject
System.ComponentModel.Component
System.MarshalByRefObject
System.Object

上面顯示了WMI對象類型的繼承層次。所以我們需求中要擴展的對象類型應該為:System.Management.ManagementObject#rootcimv2Win32_Processor

所以應當修改配置文件,重新加載更新。更新時會有一條異常
Update-FormatData : 加載格式數據文件時出錯:
Microsoft.PowerShell,C:PowershellWin32_Processor.format.ps1xml: 文件被跳過,
因為該文件已在“Microsoft.PowerShell”中出現過。

異常可以忽略,然後重新測試。

PS C:Powershell> Get-WmiObject win32_Processor

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有效了。不會影響到其他父類型對象。

Powershell使用真實的對象工作