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 |
|
方法添加成功后就可以调用了
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 |
|
查询.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 |
|
将文件保存为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有效了。不会影响到其他父类型对象。
原文地址:https://www.cnblogs.com/lsgxeva/p/9313897.html