Search code examples
powershellwhere-clauseselect-string

Return boolean from string search


I'm trying to return TRUE from searching Get-ComplianceSearch's output for 'Completed'. My code below is a simple wait loop. But I don't think I'm returning the value correctly because the loop never finishes. I'm fairly new to PowerShell. Please assist or direct.

I'm using Powershell Core 7.1. There are no errors but the Search-String condition never returns TRUE.

    try {
        $timer = [Diagnostics.Stopwatch]::StartNew()
        while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and (-not (Get-ComplianceSearch - 
Identity $searchName | Select-String 'Completed' -SimpleMatch -Quiet))) {
        Start-Sleep -Seconds $RetryInterval
        $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds, 0)
        Write-Verbose -Message "Still waiting for action to complete after [$totalSecs] 
seconds..."
        }
    $timer.Stop()
    if ($timer.Elapsed.TotalSeconds -gt $Timeout) {
        throw 'Action did not complete before timeout period.'
    } else {
        Write-Verbose -Message 'Action completed before timeout period.'
    }
} catch {
    Write-Error -Message $_.Exception.Message
}

(This is the expected output of the command Get-ComplianceSearch)

enter image description here


Solution

  • Okay, you don't want to use Select-String here (although you can, see @mklement0's helpful answer, looking at object properties is usually preferred). That is returning an object and you want to check the Status property for "Completed". Make the following change to the -not subexpression:

    (-not (Get-ComplianceSearch -Identity $searchName | Where-Object {
      $_.Status -eq 'Completed'
    }))
    

    The above can be on one line but I broke it up for readability.


    Basically, Select-String looks for content in strings. If you are looking for a particular value of an object property however, you can use Where-Object to test for a condition and return any objects matching that condition. In this case, we want to return any object that have a Status of 'Completed', so we can negate that in the if statement.


    You (or others) might be wondering how this works since Where-Object returns matching objects, but not booleans. The answer is "truthiness". PowerShell objects are "truthy", which means anything can be evaluated as a [bool].

    The following values evaluate to $false in most cases. I've included some gotchas to watch out for when relying on "truthy" values:

    • A numeric value of 0
      • A string value of 0 evaluates as $true
    • Empty arrays
    • Empty strings
      • A whitespace-only string or strings consisting only of non-printable characters evaluates as $true
    • $false
      • A string value of False evaluates as $true

    Most everything else will evaluate to $true. This is also why comparison operators are syntactically optional when checking whether a variable is $null or not. Although there are times when an explicit value check is a good idea as comparison operators compare the actual values instead of only whether the variable "is" or "isn't".

    How does this apply to the expression above then? Simple. if statements, always treat the condition expression as a [bool], no conversion required. In addition, logical operators and conditional operators also imply a boolean comparison. For example, $var = $obj assigns $obj to $var, but
    $var = $obj -eq $obj2 or $var = $obj -and $obj2 will assign $true or $false.


    So knowing the above, if Where-Object returns nothing, it's $false. If it returns a tangible object, it's $true.