当前位置: 移动技术网 > IT编程>脚本编程>Shell > Windows Powershell扩展类型系统

Windows Powershell扩展类型系统

2017年12月08日  | 移动技术网IT编程  | 我要评论
powershell一个最吸引人的功能是它能够将任何对象转换成文本,我们已经使用过将对象属性以不同的版式转换成文本,并且输出。更令人惊奇的是powershell会把最重要最

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
接下来创建一个配置文件:

复制代码 代码如下:

<configuration>
<viewdefinitions>
<view>
<name>customview</name>
<viewselectedby>
<typename>system.management.managementobject</typename>
</viewselectedby>
<tablecontrol>
<tableheaders>
<tablecolumnheader>
<label>name</label>
<width>12</width>
</tablecolumnheader>
<tablecolumnheader>
<label>description</label>
<width>30</width>
</tablecolumnheader>
<tablecolumnheader>
<label>id</label>
</tablecolumnheader>
</tableheaders>
<tablerowentries>
<tablerowentry>
<tablecolumnitems>
<tablecolumnitem>
<propertyname>deviceid</propertyname>
</tablecolumnitem>
<tablecolumnitem>
<propertyname>description</propertyname>
</tablecolumnitem>
<tablecolumnitem>
<propertyname>processorid</propertyname>
</tablecolumnitem>
</tablecolumnitems>
</tablerowentry>
</tablerowentries>
</tablecontrol>
</view>
</viewdefinitions>
</configuration>

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

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网