Powershell使用真实的对象工作

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有效了。不会影响到其他父类型对象。

 

posted @ 2018-07-15 16:19  lsgxeva  阅读(1695)  评论(0编辑  收藏  举报