Search code examples
powershellpowershell-7.0

filter Foreach-Object results with multiple conditions in Powershell not working


I'm trying to filter a list of changed files in git using Powershell 7. I want only the file paths that begin with 'packages' or 'database'. When I run the code, the results are not filtered and everything is returned. How would I get the filtering to work? I'm new to Powershell scripting.

Here is my code:

$editedFiles = git diff HEAD [git commit id] --name-only
$editedFiles | ForEach-Object {
    $sepIndex = $_.IndexOf('/')
    if($sepIndex -gt 0 -and ($_ -contains 'packages' -or 'database')) {
        Write-Output $_      
    }
}

Solution

  • Couple of things to watch out for here:

    -contains is a collection containment operator - for strings, you'll want either the -like wildcard comparison operator:

    $_ -like "*packages*"
    

    or the -match regex operator:

    $_ -match 'package'
    

    The other thing to watch out for here is the -or operator - it only takes boolean operands ($true/$false), and if you pass it anything else, it'll convert the operands to [bool] if necessary.

    That means that the following kind of statement:

    $(<# any expression, really #>) -or 'non-empty string'
    

    ALWAYS returns $true - because a non-empty string evaluates to $true when converted to [bool].

    Instead, you'll want to change two separate comparisons:

    $_ -like '*packages*' -or $_ -like '*database*'
    

    Alternatively, you can use the -match operator once, by using an alternation (|):

    $_ -match 'package|database'
    

    And end up with something like:

    $editedFiles | ForEach-Object {
        $sepIndex = $_.IndexOf('/')
        if($sepIndex -gt 0 -and $_ -match 'package|database') {
            Write-Output $_      
        }
    }
    

    If filtering is all you intend to do in the ForEach-Object block, you might as well use Where-Object - it's designed exactly for that :)

    $editedFiles | Where-Object {
        $_.IndexOf('/') -gt 0 -and $_ -match 'package|database'
    }