Wednesday, February 29, 2012

PowerShell, the ETS and Type XML

One of (the many!) cool features of PowerShell is the ETS, the Extensible Type System. With the ETS, you are able to create new types and, perhaps more importantly, extend existing types simply and easily.  The ETS is one of the key ways that PowerShell provides a level of consistency in the objects you use, despite that consistency being lacking in the underlying object(s).

Take, for example the System.Array type. This type, in .NET, has a Length property, but does not have a Count property. Why not, I hear you ask. The answer is simple: the folks that developed this type did not include it. Sadly, the .NET Framework has lots of little (potential) inconsistencies. In a perfect world, we can fix this – and with PowerShell we can!

Using a bit of XML, you can easily add an alias called Count to the Name property. This XML looks like this:

<Types>
    <Type>
        <Name>System.Array</Name>
        <Members>
            <AliasProperty>
                <Name>Count</Name>
                <ReferencedMemberName>Length</ReferencedMemberName>
            </AliasProperty>
        </Members>
    </Type>
</Types>

To add this alias, you just save this XML (say to My.Types.PS1XML) then import this definition into PowerShell by using the Update-Type cmdlet. Now as it happens you don’t need to do this for System.Array, as Microsoft has already provided this extension (and a bunch more) in $PSHome/Types.PS1XML, which is loaded by default. But I think you get the idea.

If you want to see the ETS at work, take some time to study this Types.PS1XML file – it shows  how PowerShell extends .NET specifically to provide a level of consistency across objects that is missing from the .NET Framework itself. Some cool things this file does include:

  • Provides .ToString() methods for a number of types (these definitions override the default .ToString() method in the type itself.
  • Add new properties implemented as PowerShell script blocks. For example, System.Management.Automation.PSDrive is extended with a script property called Used (but for PSdrive objects that are based on the FileSystem provider only!). 
  • Define default Property Sets for some objects. These then become the properties that PowerShell display by default. For example, the default property set for System.ServiceProcess.ServiceController is defined as; Status, Name and DisplayName. If you run the Get-Service cmdlet, you will see these three properties are displayed by default.

The ETS enables you to adapt ANY .NET type – adding in properties that don’t exist, or overriding the ones that do exist. These new properties can be a new alias (as noted above), a script property, or a script method. One particular place I’ve found recently where this is useful is on the output of Get-ADComputer. If you run Get-ADComputer (Part of the active directory module shipped by Microsoft with Server 2008 R2), the output is objects of the type Microsoft.ActiveDirectory.Management.ADComputer. This type has a Name property for the computer name (which is the host name) as well as a DNSHostName. This is all fine and well, but many of the cmdlets that take a computer name (and send the command to that computer for processing) use the ComputerName property. So how do you use Get-ADComputer to get computer names and then pipe the output to other cmdlets when the property names differ?  For example, suppose you want to find  One way is like this:

Get-ADComputer –filter * | foreach {Get-Service –name ‘DNS Server’ –computername $_.name} …

That’s a bit messy – as we wanted the computer name to be returned from Get-ADComputer and used in the call to Get-Service. By default, this is not possible. However, if we do some magic type adaptation, we could add an alias property to the ServiceController object, like this:

<Types>
<Type>
    <Name>Microsoft.ActiveDirectory.Management.ADComputer</Name>
    <Members>
          <AliasProperty>
                <Name>ComputerName</Name>
                <ReferencedMemberName>Name</ReferencedMemberName>
            </AliasProperty>           
    </Members>
</Type>
</Types>

Once you import this definition (use Update-TypeData specifying the filename including this definition), then, we can do this:

Get-AdComputer -Filter * |
   
Get-Service -Name "DNS Server" -ErrorAction SilentlyContinue |
        Format-Table MachineName, Displayname, Status –Autosize

On my system, this produces the following output:

MachineName DisplayName  Status
----------- -----------  ------
COOKHAM1    DNS Server  Running
TALLGUY2    DNS Server  Running

As you can see, Type XML is the key to exploiting the ETS. With Type XML and the ETS, POwershell and you can find consistency in places where it’s never existed. What a cool feature!

[Later]

Thanks for the comment from JB. If, like JB,  you were wondering why I a using the MachineName property in the call to Format Table. The ComputernName property is initially created on the objects produced by Get-ADComputer. The Get-Service cmdlet takes the objets from the pipeline and gets the ComputerName property the sends the service request to the computer named by computer name. However, Get-Service produces objects that return the ComputerName in a Machinename property (even though the CMDLET parameter is Computername)!  Rather demonstrating my point about consistent inconsistency! I could add another alias property to the ServiceController type, which is not a bad idea!

 

Technorati Tags: ,,

Wednesday, February 08, 2012

Get-ChildItem and the–Include and –Filter parameters

I saw a good question the other day in the PowerShell.Com Learn PowerShell Forum which related to using –Include when calling Get-ChildItem (or DIR, or LS!). The OP had a bunch of files in a folder (C:\Data) and wanted to get at just the *.txt files as follows:

Get-ChildItem –Path C:\Data –Include *.Txt

But it did not work – it returned no files at all (even though there were some in the folder. The reason is clear if you read the great help text closely: the Include switch is only active if you are also using the –Recurse parameter! Another small to make is that the –include property specifies a globbed string (I.e. a file name specified with Wild cards) and not a regular expression.

The simplest way to just get the text files form a single folder would be:

Get-ChildItem –Path C:\Data\*.Txt

Another way to get just the Text files in a given folder would be to use the –Filter parameter. The –Filter parameter  is sent to the provider and is used to qualify the –Path value. You can call it like:

Get-ChildItem –Path C:\Data\ –Filter *.Txt

So you have two ways to get a subset of files in a folder using a form of early filtering. And if you use –Recurse (thus are getting all the files in a folder and it’s child folders), you can use either the –Include or –Filter parameters. They give the same result. Well almost. If you use –Include *.txt, you only get the files which have an extension of .txt. Using  -Filter, you get a slightly different result as shown here:

Psh[Cookham8:C:\foo]>Get-ChildItem -path c:\DATA  -filter *.txt -recurse  -ea Silentlycontinue
    Directory: C:\DATA
Mode                LastWriteTime     Length Name                                                        
----                -------------     ------ ----                                      
-a---          2/5/2012   3:48 PM          0 copy.txtfoo                                                        
-a---          2/5/2012   3:48 PM          0 one.txt
-a---          2/5/2012   3:48 PM          0 three.txt
-a---          2/5/2012   3:48 PM          0 two.txt

As you can see, *.txt also copies *.txtfoo. I’m sure this is ‘by design’ but it doesn’t seem to map to my expectations. Still, in most cases, extensions do not overlap like this (of course PowerShell is an exception – with .PS1 and .PS1XML extenstions!).

As an alternative to using either –Filter or –Include, you could always get ALL the child items (e.g. all the file and folder objects in C:\Data), and pipe the output to Where-Object for further (later) filtering. This works, but as most IT admins know, early filtering tends to be more efficient. But the filtering done by –Filter may be surprising – If I use –Filter *.ps1, then PowerShell returns me all the PowerShell scripts in

The –Filter parameter also turns out to have an additional benefit. The value of Filter you specify is used by the provider to qualify the path value. By comparison, the –Include specifies a filter for PowerShell to apply. Thus, the –Filter parameter generates early, early filtering, whereas-Include is later early filtering! The performance difference between the two approaches turns out to be significant! I wrote a little script to calculate the costs of using the three methods, as follows:

# Test-Filtering.PS1
$start = get-date
$f = Get-ChildItem -path c:\windows  -include *ps1 -recurse -ea Silentlycontinue
$end = Get-Date
$time1=$end-$start
"Using -Include    : {0,4} files in {1,-6:n2} seconds" -f $f.count,$time1.totalseconds
$f| ft name,length

$start = get-date
$f=Get-ChildItem -path c:\windows  -filter *ps1 -recurse  -ea Silentlycontinue
$end = Get-Date
$time2=$end-$start
"Using -Filter     : {0,4} files in {1,-6:n2} seconds" -f $f.count,$time2.totalseconds
$f | ft name,length

$start = get-date
$f=Get-ChildItem -path c:\windows  -recurse  -ea Silentlycontinue | where {$_.extension -eq '.ps1'}
$end = Get-Date
$time3=$end-$start
"Using Where clause: {0,4} files in {1,-6:n2} seconds" -f $f.count,$time3.totalseconds

The results are here:

image

I have to say, the numbers were not what I was expecting! I was surprised how much faster –Filter turned out to be – around 3 times quicker than either –Include, or using late filtering. And compared to using –Include, late filtering is really not all that much slower.

 


Wednesday, February 01, 2012

Windows PowerShell PowerCamp–Helsinki March 10-11

Arrangements for this event in Helsinki are moving along nicely. Those very nice people at Sovelto, who are hosting the event, have put up a new web page around the event: see http://www.sovelto.fi/kurssit/Pages/PowerShell-PowerCamp.aspx (or to see this page in Finnish, go to : http://www.sovelto.fi/Kurssit/Kurssivalikoima/Pages/Kurssihaku.aspx?kurssiID=3647). These web pages have the Helsinki agenda and all the details of where/how to book, etc.

As you will see, the event is over the weekend of March 10 and 11 2010 at Sovelto’s offices in Helsinki. We’ll start at 9 both days. We will be including lunch, refreshments and breakfast both days, with a nice meal out and a bit of fun for the Saturday night (details will be announced soon – it should be fun!).

So if you are anywhere in the world and want to get a first class jump start to PowerShell, consider coming! We’ll even have PCs for all the attendees as well as a nice little goodie basket to take home!

Contact me if you have any questions – or better yet, contact Sovelto and book a place!