Hello again and welcome to the third part of my blog post series dealing with McliPSSnapIn – the PowerShell SnapIn to automate the Citrix Provisioning Services (PVS). Hope you enjoyed the first two parts and played with the McliPSSnapIn cmdlets meanwhile. This time you’ll learn how to handle the output of the Mcli-Get cmdlet meaning how to convert its structured text to an object (or an array of objects).

For example, suppose you want to display all vDisks sorted by size in descending order. By intuition, Mcli-Get DiskInfo and Sort-Object seem well-suited for this job get done.

PowerShell

1

PSC:\>Mcli-GetDiskInfo|Sort-ObjectdiskSize-Descending

Worse luck! This will end up in a mess (which I don’t want to show here). As formerly mentioned, McliPSSnapIn‘s cmdlets don’t deliver objects but rather text information just in the fashion of a legacy console utility. This is not much fun.

Since PowerShell supports the creation of custom objects you can convert Mcli-Get‘s output to your own “homebrew” object, strictly speaking to a Device, Disk, Site, Store, Farm, whatever PVS object. Look at this working solution:

Compared to the task to create an object and to assign property names with values to that object it is actually more challenging to parse the text-based information in order to identify the information related to each single object.

You’re not scared now, are you? 😉 Let’s take one thing at a time – according to Louis X divide et impera strategy.

First, let’s analyze the text returned by Mcli-Get DiskInfo on my test system:

PowerShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

PSC:\>Mcli-GetDiskInfo

Executing:Get DiskInfo

Get succeeded.3record(s)returned.

Record#1

diskLocatorId:5c3e5124-158b-44e7-891a-f600d1ebd072

diskLocatorName:VDISK

...

diskSize:41940702720

...

deviceCount:0

locked:0

Record#2

...

Record#3

...

PSC:\>

The cmdlet returned the “properties” of three vDisks. The text is structured nicely in order to support human readability:

The output begins with a short summary followed by an information block for each returned record (object).

Each information block is introduced with “Record #nnn” where nnn stands for a counter

Furthermore empty lines separate the information blocks from each other.

The lines that contain the actual data for the records (objects) are indented “name: value” pairs like “diskSize: 41940702720”

Based on this research findings you’re able design an algorithm as stated above to convert the cmdlet’s output to an array of objects.

The Power Of “switch -regex” Unveiled

Naturally you need to process the returned text data in a loop. At first glance it seems perfectly possible to use a foreach loop as follows:

PowerShell

1

2

3

4

foreach($linein$(Mcli-GetDiskInfo))

{

...

}

Inside each run it is important to distinguish between three cases:

A new information block begins (initiated by “Record #nnn“)

Information for the current record (object) in the form of “name: value“

Superfluous information (as for instance the summary)

I consider this a classic case for a switch statement with the -regex option in order to identify a match with “Record #nnn” and a match with “name: value“.

PowerShell

1

2

3

4

5

6

7

foreach($linein$(Mcli-GetDiskInfo))

{

switch-regex($line)

{

...

}

}

You know what? switch is able to process a pipeline (an array of elements) itself, meaning you can forget foreach and let switch process Mcli-Get‘s output directly:

PowerShell

1

2

3

4

switch-regex(Mcli-GetDiskInfo)

{

...

}

According PowerShell’s help, the switch statement’s -regex option “indicates that the match clause, if a string, is treated as a regex string.” So, you need two regex strings, one to identify a “Record #nnn” line and one to identify a “name: value” pair. I will explain my regex statements below.

This regex string matches “Record #nnn“: ^Record\s#\d+$

Element

Description

^

Matches the beginning characters

Record

Matches “Record”

\s

Matches any single white-space character

#

Matches “#”

\d

Matches any decimal digit

+

Matches repeating instances of the preceding characters

$

Matches the end characters

This regex string matches “name: value“: ^\s{2}(?\w+):\s(?.*)

Element

Description

^

Matches the beginning characters

\s{2}

Matches exactly two white-space characters

(?<Name>\w+)

Matches any word character until the next non-word character and assign the result to $Matches.Value

:

Matches “:”

\s

Matches any single white-space characters

(?<Value>.*)

Matches the rest of the line and assign the result to $Matches.Value

And what is $Matches? PowerShell places resulting matches automatically in the $Matches hashtable where $Matches[0] always holds the entire match. Furthermore, PowerShell allows to specify (optionally named) capture groups inside a regex string. These captures will be assigned to separate elements of the $Matches hashtable, either numbered or named. The example below shows what I’m trying to explain:

PowerShell

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

PSC:\># Unnamed capture groups in a regex string

PSC:\>' theName: the Value'-match'^\s{2}(\w+):\s(.*)'

True

PSC:\>$Matches

Name Value

---------

2the Value

1theName

0theName:the Value

PSC:\># Named capture groups in a regex string

PSC:\>' theName: the Value'-match'^\s{2}(?<Name>\w+):\s(?<Value>.*)'

True

PSC:\>$Matches

Name Value

---------

Value the Value

Name theName

0theName:the Value

PSC:\>$Matches.Name

theName

PCC:\>

This is so cool! Did I already mention that I love PowerShell?

How To Avoid Flattening Of Arrays?

I need to tell you that the return value of the above-stated Get-DiskInfo function messed me around a while.

The function should always return an array regardless of the number of objects found by Mcli-Get. The problem I was running into was that the function didn’t returned an array when the object count was 1. Instead it returned the object directly.

In order to avoid that PowerShell unrolls or flattens the array when it would contain only one element I used the comma operator to force the function to return the array as is:

PowerShell

1

return,$diskInfo

Ah, I’ve missed to show the Get-DiskInfo function in action:

PowerShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

PSC:\># Is the function loaded?

PSC:\>test-pathfunction:pvs\get-diskinfo

True

PSC:\># assign the returning objects to $vdisks

PSC:\>$vdisks=pvs\get-diskinfo

PSC:\># show disk sizes in gb

PSC:\>$vdisks|%{

>>'Disk Name: {0}'-f$_.diskLocatorName

>>'Disk Size: {0:N2}'-f($_.diskSize/1gb)

>>}

>>

Disk Name:VDISK

Disk Size:39.06

Disk Name:TestDisk

Disk Size:10.00

Disk Name:VDISK2

Disk Size:39.06

PSC:\>

The Get-DiskInfo function is a very basic example for creating custom objects in PowerShell and therefore should considered to be a kick-start.

3 Comments

Convert MCli output into PowerShell Objects

February 29, 2012 at 9:35 pm

[…] text output.A google search for a method to quickly convert the garbage output to objects led me to this blog post by Frank Peter. He describes a clever use of the switch statement with regular expressions with the […]

Convert Citrix PVS Mcli-Get Output To Objects | out-web.net

May 17, 2013 at 10:56 pm

[…] Whatever, today I want to share a generalized version of my function that converts a text array (Mcli-Get output) to PowerShell/.NET objects. For more background information and explanation how the function below works read my former blog post Citrix Provisioning Services 5.1’s PowerShell Interface, Part III […]