Effective PowerShell Item 15: Using the Output Field Separator $OFS

$OFS is the “output field separator” variable.  Whatever value it contains will be used as the string separator between elements of an array that is rendered to a string.  For example, consider the following array definition and subsequent rendering to string:

PS> $array = 1,2,3
PS> "$array"

What would you expect the resulting string to be?  Here’s the output:

1 2 3

How does PowerShell go about rendering elements of an array into a single string?  It is pretty simple as you would expect.  Each element is converted to its string representation.  The only other detail left is to determine what characters to use to separate each element in the final string.  The $OFS variable is not initially created by PowerShell and if it doesn’t exist, PowerShell uses a single space character to separate elements as you can see in the example above.  What is neat is that PowerShell gives you the ability change the separator string by setting the $OFS variable like this:

PS> $OFS = ‘, ‘
PS> "$array"
1, 2, 3

Note that the separator doesn’t have to be single character.  It doesn’t even have to be a string, but in the end whatever value that is assigned to $OFS is converted to a string e.g.:

PS> $OFS = $true
PS> "$array"
1True2True3

This is an admittedly weird example.  In the common case, you will just assign a string to $OFS like “, “ or “`t” or “`n”, etc. 

$OFS also works for multi-dimensional arrays e.g.:

PS> $array = new-object ‘int[,]’ 2, 3
PS> $array[0,0] = 1
PS> $array[0,1] = 2
PS> $array[0,2] = 3
PS> $array[1,0] = 4
PS> $array[1,1] = 5
PS> $array[1,2] = 6
PS> $OFS = ‘, ‘
PS> "$array"
1, 2, 3, 4, 5, 6

Unfortunately, $OFS doesn’t work so well for jagged arrays:

PS> $array = @(@(1,2),@(3,4))
PS> $OFS = ‘, ‘
PS> "$array"
System.Object[], System.Object[]

# Let’s try a different approach – not so satisfying
PS> "$($array[0]), $($array[1])"

1, 2, 3, 4

When I see folks use [string]::Join() or –join in version 2 of PowerShell, I wonder if it would be better to use $OFS and string rendering.  Here is an example I came across recently:

$typeDecls  = @($_.GetGenericArguments() | %{"[string]`$Of" + $_.Name}) –join ‘, ‘
$paramDecls = @($_.GetParameters() | % { "[$($_.ParameterType)]`$$($_.Name)" }) –join ‘, ‘

$decls = $typeDecls
$decls += $(if ($decls –and $paramDecls) { ‘, ‘ })
$decls += $(if ($paramDecls) { $paramDecls })

function New-$fname($decls) { … }

Using $OFS the script changes to:

$OFS = ‘, ‘
$typeDecls  = @($_.GetGenericArguments() | %{"[string]`$Of" + $_.Name})
$paramDecls = @($_.GetParameters() | % { "[$($_.ParameterType)]`$$($_.Name)" })

$decls = $typeDecls + $paramDecls

function New-$fname("$decls") { … }

In this example, the use of $OFS shines because you benefit by delaying the string rendering of the arrays until the last moment.  In this case, I wanted to keep both $typeDecls and $paramDecls as arrays so that they could be concatenated together and then rendered as a string containing a comma separated list.  If these two variables had been converted to strings earlier, as in the “before” script above, then you need special case logic in the event $typeDecls and/or $paramDecls are empty.

About these ads
This entry was posted in Effective PowerShell. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s