Using Null Conditional Access Operators in PowerShell 7

Some of the common reasons we check if our variable is null or not, are because we have been trying to access an property on an object which does not exists or misspelled, the object itself does not exist, or iterating through an collection where some members are null or invalid, etc. To deal with these kind of situations, we had to use if..else condition blocks. PowerShell 7 introduced two experimental null conditional access operators: ?. and ?[]. However to enable and use them, you had to use Enable-ExperimentalFeature cmdlet. These have been moved to GA with release of version 7.1.

Before PowerShell 7

Let’s say, we are trying to identify if a service named TeamCity Server exists on a machine and if yes, then stop it. The usual code goes like this:

$service = Get-Service -DisplayName "TeamCity Server"
$service.Stop()

Now if the service won’t exists for some reason or say, more likely that you have misspelled service name, it will throw error like below:

InvalidOperation: 
 Line |
    2 |  $service.stop()
      |  ~~~
      | You cannot call a method on a null-valued expression.

Same goes out calling with array members.

After PowerShell 7

If you are using PowerShell 7, you first need to enable experimental features:

Enable-ExperimentalFeature -Name PSNullConditionalOperators

If you are using PowerShell 7.1, these features are already on. So you can directly use these operators. Note that the ?. and ?[] operators are member access operators and do not allow a space in between the variable name and the operator.

With PowerShell 7, above code can be re-written as below:

$service = Get-Service -DisplayName "TeamCity Server"
${service}?.Stop()

Since PowerShell allows ? as part of the variable name, disambiguation is required when the operators are used without a space between the variable name and the operator. To disambiguate, the variables must use {} around the variable name as we have used above.

Same goes for working on collections. Let’s say you are expecting a function to return list of 4 services and then iterate through them to find their status. It works fine as long as function continues to return list of services but it will fail if for some reason function returns null (for sake of simplicity, instead of calling function, we have directly defined array in below code):

# this works - 
$services = "winrm","wirrms","winmgmt","wlansvc" 
for($count=0;$count -lt 3;$count++){
    $status = (Get-Service -Name $services[$count] -ErrorAction SilentlyContinue).Status
    Write-Host "Service $($services[$count]) is $status"
}

# this will throw error -
$services = $null
for($count=0;$count -lt 3;$count++){
    $status = (Get-Service -Name $services[$count] -ErrorAction SilentlyContinue).Status
    Write-Host "Service $($services[$count]) is $status"
}

The error message will be like below:

InvalidOperation: 
 Line |
    3 |      $status = (Get-Service -Name $services[$count] -ErrorAction Silen …
      |      ~~~~~~~~~~~~~
      | Cannot index into a null array.

If we null conditional access operator, we can re-write our code like below, which will work regardless of what is returned:

# this works -
$services = $null
for($count=0;$count -lt 3;$count++){
    $status = (Get-Service -Name ${services}?[$count] -ErrorAction SilentlyContinue).Status
    Write-Host "Service $(${services}?[$count]) is $status"
}

Stretching things

We can chain these operators, as long as you continue to follow the proper semantics as mentioned above. However beyond 2-3 operators in chaining, code could become harder to read to the normal set of eyes.

3 thoughts on “Using Null Conditional Access Operators in PowerShell 7

  1. First example you gave will throw an error on first line not a second when you are trying to assign variable to non-existent service rather when you are trying to stop it. First line needs to be appened with -ErrorAction SilentlyContinue

    Like

    • Both of them will throw errors:
      PS D:\mohit\src\pwsh7> $service = Get-Service -DisplayName “TeamCitee Server”

      Get-Service: Cannot find any service with display name ‘TeamCitee Server’.
      PS D:\mohit\src\pwsh7> $service.stop()

      InvalidOperation: You cannot call a method on a null-valued expression.

      Like

  2. So for pre-powershell 7, this one liner will work from cl, though in script I would make it three: $service = Get-Service -DisplayName „TeamCity Server“ -EA SilentlyContinue; try { $service.Stop(); Write-host „$service service stopped.“ } catch { Write-Host „Invalid Service Display Name specified“}

    Like

Leave a comment