I've had a few comments and emails lately about my post and script on converting text to objects. I decided the function needed a little more lovin' so today I have an updated version, complete with comment based help.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Function Convert-TextToObject {
<# .Synopsis Convert text to a PowerShell object .Description This function takes a collection of simple delimited lines and turns them into an object. The function assumes a single delimiter. The default delimiter is the colon (:). The item to the left will be the property and the item on the left will be the property value. By default the function will split on every delimiter, but you can use the -SplitCount parameter to control the number of strings. If you don't want to split at all use a value of 1. To split on the first delimiter only, use a value of 2. For example, if the string is: Foobar: 01/11/2012 09:41:28 and you tried to split on the ":", you would end up with 4 elements in the array. But using a -SplitCount value of 2 would give you this: Foobar 01/11/2012 09:41:28 Use $GroupCount to keep track of items that come in groups and write a new object when the count has been reached. For example, this allows you to pipe in a long collection of strings and turn every 5 into an object. This function works best with command line tools that write list-like output. .Parameter Text The text to be converted .Parameter Delimiter The text delimiter to split on. The colon (":") is the default. .Parameter SplitCount The number of strings to create when splitting. The default is to split on all delimiters. If you don't want to split at all use a value of 1. To split on the first delimiter only, use a value of 2. .Parameter GroupCount The number of piped in strings to group together to form a new object. .Example PS C:> tasklist /s server01 /fo list | where {$_} | convert-texttoobject -group 5
Take the output for Tasklist.exe as a list, strip out blank lines and pipe
to Convert-TextToObject. Turn every 5 items into an object.
.Example
PS C:\> get-content c:\work\data.txt | select -skip 4 | where {$_} | cto -group 4 -SplitCount 2 | format-list
LinkDate : 11/20/2010 5:44:56 AM
DisplayName : 1394 OHCI Compliant Host Controller
DriverType : Kernel
ModuleName : 1394ohci
LinkDate : 11/20/2010 4:19:16 AM
DisplayName : Microsoft ACPI Driver
DriverType : Kernel
ModuleName : ACPI
LinkDate : 11/20/2010 4:30:42 AM
DisplayName : ACPI Power Meter Driver
DriverType : Kernel
ModuleName : AcpiPmi
Get the Data.txt file, skipping the first 4 lines and stripping out blanks.
Then create objects for every 4 lines. The SplitCount is set to 2 so that
the LinkDate value is the complete datetime stamp.
.Example
PS C:\> whoami /user /fo list | where {$_ -match ":"} | convert-texttoobject | format-table -auto
UserName SID
-------- ---
serenity\jeff S-1-5-21-2858895768-3673612314-3109562570-1000
.Example
PS C:\> whoami /groups /fo list | where {$_ -match ":"} | cto -group 4 | format-list
Attributes : Mandatory group, Enabled by default, Enabled group
Type : Well-known group
GroupName : Everyone
SID : S-1-1-0
Attributes : Mandatory group, Enabled by default, Enabled group, Group owner
Type : Alias
GroupName : BUILTIN\Administrators
SID : S-1-5-32-544
...
Get group listing from Whoami.exe and filter out lines that don't have a colon.
Create objects for every 4 lines. This example is using the cto alias for the
Convert-TexttoObject function.
.Link
about_Split
New-Object
.Inputs
Strings
.Outputs
Custom object
#>
[cmdletbinding(SupportsShouldProcess=$True)]
param (
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a string to be parsed into an object",
ValueFromPipeline=$True)]
[ValidateNotNullorEmpty()]
[string[]]$Text,
[Parameter(Position=1)]
[ValidateNotNullOrEmpty()]
[string]$Delimiter=":",
[ValidateScript({$_ -ge 0})]
[int]$SplitCount=0,
[int]$GroupCount
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
#define a hashtable
$myHash=@{}
if ($GroupCount) {
Write-Verbose "Grouping every $GroupCount items as an object"
}
#start an internal counter
$i=0
Write-Verbose "Skipping $Skip lines"
Write-Verbose "Using $Delimiter delimiter"
Write-Verbose "Splitting into $SplitCount lines. 0 means all."
}
Process {
Foreach ($item in $text) {
if ($i -lt $GroupCount) {
$i++
}
else {
#reset
$i=1
}
#split each line at the delimiter
$data=$item -Split $delimiter,$SplitCount
#remove spaces from "property" name
$prop=$data[0].Replace(" ","")
#trim
$prop=$prop.Trim()
$val=$data[1].Trim()
#add to hash table
Write-Verbose "Adding $prop to hash table with a value of $val"
$myHash.Add($prop,$val)
#if internal counter is equal to the group count
#write the object and reset the hash table
if ($i -eq $groupCount) {
New-Object -TypeName PSObject -Property $myHash
$myHash.Clear()
}
} #foreach
}
End {
#create new object from hash table
if ($myHash.count -gt 0) {
New-Object -TypeName PSObject -Property $myHash
Write-Verbose "Ending $($myinvocation.mycommand)"
}
}
} #end function
Check out the original article to understand the basics. The major change here is the SplitCount parameter. Often you might end up with a line of text like this:
LinkDate : 11/20/2010 4:30:42 AM
The function needs to split the string into an array on the colon. But when you do that, the time stamp will "break". The answer is to tell the Split operator how many strings to create. The default of 0 will split into all strings.
PS C:\> "DisplayName : ACPI Power Meter Driver" -split ":",0
DisplayName
ACPI Power Meter Driver
If I try that with the time stamp string you see the problem.
PS C:\> "LinkDate : 11/20/2010 4:30:42 AM" -split ":",0
LinkDate
11/20/2010 4
30
42 AM
But now I can specify the number of strings and I get what I need.
PS C:\> "LinkDate : 11/20/2010 4:30:42 AM" -split ":",2
LinkDate
11/20/2010 4:30:42 AM
The new version of the function incorporates this. I also received a comment about including a skip feature. I kicked that idea around for awhile, but decided in the end that PowerShell already had a mechanism for skipping lines. I wanted to keep my function limited to a single purpose. So if your text output includes a number of lines you want to skip, before you begin converting text to object, use something like this:
PS C:\> get-content c:\work\data.txt | select -skip 4 | where {$_} | cto -group 4 -SplitCount 2 | format-list
LinkDate : 11/20/2010 5:44:56 AM
DisplayName : 1394 OHCI Compliant Host Controller
DriverType : Kernel
ModuleName : 1394ohci
LinkDate : 11/20/2010 4:19:16 AM
DisplayName : Microsoft ACPI Driver
DriverType : Kernel
ModuleName : ACPI
LinkDate : 11/20/2010 4:30:42 AM
DisplayName : ACPI Power Meter Driver
DriverType : Kernel
ModuleName : AcpiPmi
The script file with the function, will also create an alias of cto. I hope you'll download Convert-TexttoObject.-v1.5 and let me know what you think.
Typo in the synopsis…
The item to the left will be
the property and the item on the leftwill be the property value.
I assume you mean:
The item to the left will be
the property and the item on the right will be the property value.
Yes. Unless you are south of the equator.
I played around a little with Convert-TexttoObject function and got stuck suddenly. I wrote small script(tried on W8K R2 and W7) which called Convert-TexttoObject twice with two data files. First one gets result as expected but second call returns nothing. It was simple script with data*.txt containing similar things as your example
. .\Convert-TextToObject.ps1
$result = get-content .\data.txt | where {$_} | Convert-TextToObject -group 4 -SplitCount 2
$result
“—————-”
#
$result = get-content .\data1.txt | where {$_} | Convert-TextToObject -group 4 -SplitCount 2
$result
And the output was always following:
LinkDate DisplayName DriverType ModuleName
——– ———– ———- ———-
11/20/2010 4:19:… Microsoft ACPI D… Kernel ACPI
11/20/2010 4:30:… ACPI Power Meter… Kernel AcpiPmi
—————-
—————-
The empty space should be result from second call to function.
It’s kind of weird situation because if I split script to two parts and ran these separately and manually from Powershell prompt then all was normal. But if I did additional script which called these two:
— testscript —
& ‘.\test2.ps1’
#
& ‘.\test3.ps1’
———
Then result was same, second script returned empty result. I gave up on that one and try to think some other way to do things right now.
Sometimes PowerShell’s formatting get’s funky. I’ve seen where output from the second command is formatted based on a previous command. What you might try is in your script pipe each command to Out-Host. Normally this happens automatically but I think I’ve resorted to this in the past to get around the formatting glitch.
Damned formatting, haven’t seen this glitch before. Thanks for the Out-Host hint, it was helpful.