ConvertFrom-String
provides separator-based parsing as well as heuristics-based parsing based on templates containing example values. The separator-based parsing applies automatic type conversions you cannot control, and the template language is poorly documented, with the exact behavior hard to predict - it's best to avoid this cmdlet altogether. Also note that it's not available in PowerShell [Core] v6+.
Instead, I suggest an approach based on the switch
statement[1] and the -split
operator to create a collection of custom objects ([pscustomobject]
) representing the log lines:
# Use $objects = switch ... to capture the generated objects in a variable.
switch -File .\testfile.log { # Loop over all file lines
default {
$oht = [ordered] @{ } # Define an aux. ordered hashtable
foreach ($keyValue in -split $_) { # Loop over key-value pairs
$key, $value = $keyValue -split '=', 2 # Split pair into key and value
$oht[$key] = $value -replace '^"|"$' # Add to hashtable with "..." removed
}
[pscustomobject] $oht # Convert to custom object and output.
}
}
Note:
The above assumes that your values have no embedded spaces; if they do, more work is needed - see next section.
To capture the generated custom objects in a variable, simply use $objects = switch ...
- With two ore more log lines,
$objects
becomes an [object[]]
array of [pscustomobject]
instances. If you want to ensure that $objects
also becomes an array even if there happens to be just one log line, use [array] $objects = switch ...
([array]
is effectively the same as [object[]]
).
To directly send the output objects through the pipeline to other cmdlets, enclose the switch
statement in & { ... }
With your sample input, this yields:
date srcip destip srcintf
---- ----- ------ -------
2019-12-02 8.8.8.8 8.8.4.4 port2
2019-12-01 8.8.8.8 8.8.4.4 port2
2019-12-03 8.8.8.8 8.8.4.4 port2
2019-12-05 8.8.8.8 8.8.4.4 port2
2019-12-07 8.8.8.8 8.8.4.4 port2
Variant with support for values with embedded spaces inside "..."
(e.g., srcintf="port 2"
):
switch -file .\testfile.log {
default {
$oht = [ordered] @{ }
foreach ($keyValue in $_ -split '(\w+=(?:[^"][^ ]*|"[^"]*"))' -notmatch '^\s*$') {
$key, $value = $keyValue -split '=', 2
$oht[$key] = $value -replace '^"|"$'
}
[pscustomobject] $oht
}
}
Note that there's no support for embedded escaped "
instances (e.g, srcintf="port \"2\""
won't work).
Explanation:
$_ -split '(\w+=(?:[^"][^ ]*|"[^"]*"))'
splits by a regex that matches key=valueWithoutSpaces
and key="value that may have spaces"
tokens and, by virtue of enclosing the expression in (...)
(creating a capture group), includes these "separators" in the tokens that -split
outputs (by default, separators aren't included).
-notmatch '^\s*$'
then weeds out empty and all-spaces tokens from the result (the "data tokens", which aren't of interest in our case), leaving effectively just the key-value pairs.
$key, $value = $keyValue -split '=', 2
splits the given key-value token by =
into at most 2 tokens, and uses a destructuring assignment to assign the key and the value to separate variables.
$oht[$key] = $value -replace '^"|"$'
adds an entry to the aux. hashtable with the key and value at hand, where -replace '^"|"$'
uses the -replace
operator to remove "
from the beginning and end of the value, if present.
[1] switch -File
is a flexible and much faster alternative to processing a file line by line with a combination of Get-Content
and ForEach-Object
.