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,FullNameMode 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> $objA - 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_processorName Description ID ---- ----------- -- CPU0 x64 Family 6 Model 15 Stepp... BFEBFBFF000006FD
但是这样的定义可能有个缺点,当我们获取其它WMI对象时,也会根据我们定义的规则显示。
PS C:Powershell> Get-WmiObject Win32_ShareName 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_ProcessorName 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有效了。不会影响到其他父类型对象。
仅供参考,我是一个TYPO3菜鸟:)我在我的TYPO3个人网站上做我自己的extbase/流体扩展来管理画廊。现在,我下面这个留档:开发与Extbase和流体TYPO3扩展 目前,我只在Domain/Model中构建了2个类: 加利。php 变量已设置,getter/setter也已设置。 我很清楚我需要在一段关系中把两者联系起来(“1: n”?)但是我不使用扩展生成器,我想自己理解如何做到这一点
为什么要扩展mongoose模型? 我们对业务进行分层处理 service(多模型操作) -> dao(单一模型操作) -> model(模型定义) 所以我们在dao层需要很多单一模型的数据库操作方法的封装,如果业务非常复杂,比如一个超级查询方法,然后又有各种具体业务定义方法,难道我们一个一个的都写在dao层么? 事实上dao只做暴露给service的方法,而具有一定业务约定的方法是可以放到mo
问题内容: 我想为此创建一个扩展名,仅影响… 我了解这些说明是相关的,但不确定如何: 扩展通用类型时,不提供类型参数列表作为扩展定义的一部分。而是在扩展程序的正文中提供原始类型定义的类型参数列表,并且原始类型参数名称用于引用原始定义的类型参数。 基本上,我正在尝试使用此方法: 在没有参数的情况下采取行动…这可能吗? 问题答案: Swift 3.1更新 从Swift 3.1(Xcode 8.3 be
如若引入扩展整型,到时将有一整套的规则说明应该如何进行整型扩展(及其精度)。 // 译注: 作稿之时,扩展整型尚未确定是否纳入C++11 参见: [06-0058==N1988] J. Stephen Adamczyk: Adding extended integer types to C++ (Revision 1) . (翻译:lianggang jiang)
问题内容: 我正在尝试重构M类型确实扩展了任何内容的类和子类集,即使我们知道它必须是某种类型的子类也是如此。该类型已参数化,我希望其参数化类型可用于已经具有M值的子类。 有什么方法可以定义此类,而不必在参数列表中包括冗余的K和V泛型类型。我希望能够使编译器从子类映射到的M中推断出它们。 换句话说,我希望类声明看起来像这样: 从M的定义可以推断出K和V的类型。 问题答案: 问题在于,它们并没有真正地
问题内容: Java是否可以让类扩展泛型,以便您可以将方法注入通过代码传递的任何类中?(或者是否有其他方法可以使用Java将方法注入或重写到现有类中?) 我所说的“扩展通用类型”是这样的(类“ Textended GameObject”属于游戏,可能不会更改,并且是未知的,因为它是在运行时(从其他mods)加载到游戏中的): onTick由GameEngine调用,通过这种方式,我可以将每个现有的