2

I'm doing a thing where I pick apart an XML document and write the text content of some elements to a file. The basic approach is dirt-simple once I have the XML document loaded: all I do is things like

$DI_XMLResponse.result.gudid.device.versionModelNumber | Out-File -FilePath $OutputFile -Append -NoClobber -Encoding ascii

but I need to arrange for an empty line to be emitted in the case where the leaf element has no content, or does not exist at all.

I could assign the content to a variable, test whether the variable is null, and and if so make it be an empty string, and then write it out to the file,

$foo = $DI_XMLResponse.result.gudid.device.versionModelNumber if (!$foo) $foo = '' $foo | Out-File -FilePath $OutputFile -Append -NoClobber -Encoding ascii

but I'm hoping to find some more compact sort of expression. Is there one?

mklement0
  • 382,024
  • 64
  • 607
  • 775
wades
  • 927
  • 9
  • 24

3 Answers3

9

Update:

  • PowerShell (Core) 7.0 introduced the ternary operator
    ($condition ? $valueIfTrue : $valueIfFalse) and the null-coalescing operators ($possibleNullValue ?? $valueIfNull and $possibleNullValue ??= $valueIfNull}

  • PowerShell (Core) 7.1 introduced the null-conditional operators (${possibleNullValue}?.Property and ${possibleNullValue}?[0])

    • Note the unfortunate need for {...} around the variable name, because - counterintuitively - ? is currently a legal variable-name character - see GitHub issue #14025.

The rest of this answer shows ways to emulate the ternary operator and null-coalescing for PowerShell versions 6.x and earlier:


Unfortunately, PowerShell does not have a ternary [conditional] operator (e.g.,
$condition ? $valueIfTrue : $valueIfFalse) or a null-coalescing operator (e.g.,
$possibleNullValue ?? $valueIfNull)
as of PowerShell Core 6.1.0.

However, enhancing the language to support these is being proposed.
If you'd like to see that happen, make your voice heard there - even a simple "thumbs up" helps.

In your specific case, you're looking for the null-coalescing operator, and there are - suboptimal - workarounds:


Define a custom function, as shown in Ryan Schlueter's answer for the ternary operator.

For the null-coalescing operator it could be something like:

function ?? ($PossiblyNull, $ValueIfNull) { 
  if ($null -eq $PossiblyNull) { $ValueIfNull } else { $PossiblyNull }
}

Applied to your code:

?? $DI_XMLResponse.result.gudid.device.versionModelNumber '' |
  Out-File -FilePath $OutputFile -Append -NoClobber -Encoding ascii 

The down-sides of custom functions are:

  • You need to include or import them into in every piece of code you want to use them in.

  • If you choose familiar names such a ?: and ??, the placement of operands can get confusing, because you must (invariably) place them differently in PowerShell, given the implementation as a function (e.g.,
    a ?? b in C# vs. ?? $a $b in PowerShell).


Use a helper array with implicit Boolean-to-integer conversion:

$foo = $DI_XMLResponse.result.gudid.device.versionModelNumber
($foo, '')[$null -eq $foo] | Out-File -FilePath $OutputFile -Append -NoClobber -Encoding ascii
  • ($foo, '') is an array containing the two possible values to output.
  • $null -eq $foo is conditional that evaluates to $False, if $foo is $null, and $False otherwise
    • Note that $null is deliberate placed on the LHS, because on the RHS it would perform a different test if $foo happened to be array-valued.
  • In the context of array indexing, an [int] value is expected, so $True is automatically converted to 1, whereas $False results in 0.
  • The net result is that if $foo is $null, '' is output; otherwise, $foo is used as-is.

The down-sides of this approach:

  • No short-circuiting logic is applied; i.e., both array elements are unconditionally evaluated before the appropriate element is chosen.

  • If null-coalescing is all that is needed, you must store the value to test for $null in a variable first; e.g., to emulate
    (Get-Foo Bar) ?? '(none)'
    you must first save the output from Get-Foo Bar in an auxiliary variable, so that you can write:
    $aux = Get-Foo Bar; ($aux, '(none)')[$null -eq $aux]

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

This is my go to is adding a small cmdlet. PowerShell Inline If (IIf)

$foo = $null;
Write-Host (IIf $foo "True" "False")

Function IIf($If, $Right, $Wrong) {If ($If) {$Right} Else {$Wrong}}
Ryan Schlueter
  • 2,223
  • 2
  • 13
  • 19
1

Assignments can be evaluated as expressions. What this means for you is you can transform what you've done if all you care about is brevity over readability:

if (-not ($foo = $xml. ...)) {
    $foo = ''
}
Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63