PowerShell ConvertFrom-String: Parse Unstructured Text

PowerShell ConvertFrom-String: Parse Unstructured Text

PowerShell Tips Editor 4 min read
PowerShell ConvertFrom-String: Parse Unstructured Text

Third-party tools, legacy commands, and network utilities return plain text — not objects. Converting that text into structured PowerShell objects is the bridge that makes unstructured output usable in pipelines and reports. This post covers the practical techniques for how to PowerShell parse unstructured text: string splitting, regex named groups, parsing real-world command output, and building a reusable parser function.

Quick Answer / TL;DR

For simple space-delimited output, use -split '\s+'. For fields with known positions, use regex named groups with (?<GroupName>pattern). Avoid ConvertFrom-String for production scripts — its template syntax is complex and the cmdlet is experimental.

Split-Based Parsing

Many command-line tools output fixed or whitespace-delimited columns. The -split operator and .Split() method break a line into an array by a delimiter. Access columns by array index. This works well for outputs with consistent, predictable structure.

# Parse simple space-delimited output
$output = 'web01    10.0.1.20    Windows Server 2022    Running'
$cols   = $output -split '\s{2,}'   # split on 2+ spaces

[PSCustomObject]@{
    Name    = $cols[0]
    IP      = $cols[1]
    OS      = $cols[2]
    Status  = $cols[3]
}

# Parse CSV-like line
$line   = 'jsmith,John Smith,IT,[email protected]'
$fields = $line -split ','
[PSCustomObject]@{ SAM = $fields[0]; Name = $fields[1]; Dept = $fields[2]; Email = $fields[3] }

Regex Named Groups for Field Extraction

Named capture groups make parsing self-documenting. Define a group with (?<Name>pattern) and access the captured value through the automatic $Matches hashtable after a successful -match operation.

# Parse a log entry with named groups
$logLine = '[2024-03-15 14:32:01] ERROR: Connection to 10.0.0.5 failed after 3 retries'

$pattern = '^\[(?<Date>\d{4}-\d{2}-\d{2}) (?<Time>\d{2}:\d{2}:\d{2})\] (?<Level>\w+): (?<Message>.+)$'

if ($logLine -match $pattern) {
    [PSCustomObject]@{
        Date    = $Matches.Date
        Time    = $Matches.Time
        Level   = $Matches.Level
        Message = $Matches.Message
    }
}
Date       Time     Level Message
----       ----     ----- -------
2024-03-15 14:32:01 ERROR Connection to 10.0.0.5 failed after 3 retries

Parse ipconfig Output to Objects

ipconfig returns multi-line blocks per adapter. A practical approach reads the entire output with -Raw, splits on adapter boundaries, then parses each block for IP, subnet, and gateway values.

$ipconfig = ipconfig /all

$adapters = @()
$current  = $null

foreach ($line in $ipconfig) {
    if ($line -match '^[A-Za-z]' -and $line -match ':$') {
        if ($current) { $adapters += $current }
        $current = [PSCustomObject]@{ Adapter = $line.TrimEnd(':'); IPv4 = ''; Subnet = ''; Gateway = '' }
    }
    if ($current) {
        if ($line -match 'IPv4 Address.*:\s+([\d.]+)')           { $current.IPv4    = $Matches[1] }
        if ($line -match 'Subnet Mask.*:\s+([\d.]+)')            { $current.Subnet  = $Matches[1] }
        if ($line -match 'Default Gateway.*:\s+([\d.]+)')        { $current.Gateway = $Matches[1] }
    }
}
if ($current) { $adapters += $current }

$adapters | Where-Object IPv4 | Format-Table -AutoSize

Parse netstat Output

netstat -an outputs listening and established connections. Parsing it reveals which ports are open and which processes are connecting. The key pattern is whitespace-delimited columns with consistent positions.

$connections = netstat -an | Select-Object -Skip 4 | ForEach-Object {
    $parts = $_ -split '\s+' | Where-Object { $_ }
    if ($parts.Count -ge 4) {
        [PSCustomObject]@{
            Protocol    = $parts[0]
            LocalAddress = $parts[1]
            RemoteAddress = $parts[2]
            State       = $parts[3]
        }
    }
}

# Find all listening ports
$connections | Where-Object State -eq 'LISTENING' |
    Select-Object Protocol, LocalAddress | Sort-Object LocalAddress

ConvertFrom-String with Templates

ConvertFrom-String uses example-based templates to extract data from text. You provide sample input lines where desired values are marked with {prop*} syntax. While conceptually elegant, the cmdlet is experimental, its template syntax is fragile, and regex-based parsing is more reliable in practice. For most production scenarios, regex or split-based approaches are preferable.

# ConvertFrom-String template example (experimental feature)
$template = @'
{[string]Name*} {[string]IP} {[string]Status}
'@

$data = @'
web01 10.0.1.20 Running
db01  10.0.2.30 Stopped
'@

$data | ConvertFrom-String -TemplateContent $template

Build a Generic Text Parser Function

A reusable parser function accepts a pattern, an array of header names, and input lines. It applies the pattern to each line and creates objects with the matched groups mapped to the header names.

function ConvertTo-ParsedObject {
    [CmdletBinding()]
    param(
        [string[]]$Lines,
        [string]$Pattern,
        [string[]]$Headers
    )

    foreach ($line in $Lines) {
        if ($line -match $Pattern) {
            $obj = [ordered]@{}
            for ($i = 0; $i -lt $Headers.Count; $i++) {
                $obj[$Headers[$i]] = $Matches[$i + 1]
            }
            [PSCustomObject]$obj
        }
    }
}

# Example: parse 'ping' summary lines
$pingOutput = ping -n 1 google.com | Where-Object { $_ -match 'time=' }
ConvertTo-ParsedObject -Lines $pingOutput `
    -Pattern 'time=(\d+)ms TTL=(\d+)' `
    -Headers @('RTT_ms','TTL')

Common Errors and Fixes

  • ConvertFrom-String is experimental and syntax complex — regex often simpler. ConvertFrom-String requires exact template formatting and breaks on minor output variations. For production scripts, prefer -match with named groups or -split, which give you full control and predictable behavior.
  • Multi-line output needs -Raw Get-Content. When parsing output that spans multiple lines (like ipconfig adapter blocks), use Get-Content -Raw to get the entire file as one string, or collect command output into an array first. Piping line by line loses context between lines.

Related Cmdlets / See Also

Wrapping Up

Text parsing is a core PowerShell skill for anyone working with legacy tools, network utilities, or log files. Start with -split for simple delimited output, use regex named groups for complex line formats, and build reusable parser functions for tools you query repeatedly. Skip ConvertFrom-String for production work and use regex instead.

Send-Item -To