Search code examples
powershellget-childitem

Get-ChildItem -File -Exclude issue


Issue

I wrapped Get-ChildItem in a generic function to add checks and be able to re-use it. When combining -File with -Exclude, the function seems to break even though the same call to Get-ChildItem outside of it does work as expected.

Question

What is going wrong? How can I fix it?

Related

Get-ChildItem Exclude and File parameters don't work together

MWE

function Get-Object {
  [CmdletBinding ()]
  param (
    [Parameter (
      Position    = 1,
      Mandatory   = $true,
      HelpMessage = "Path to the object"
    )]
    [String]
    $Path,
    [Parameter (
      Position    = 2,
      Mandatory   = $false,
      HelpMessage = "Type of object"
    )]
    [ValidateSet ("All", "File", "Folder")]
    [String]
    $Type = "All",
    [Parameter (
      Position    = 3,
      Mandatory   = $false,
      HelpMessage = "Filter to apply"
    )]
    [String]
    $Filter = "*",
    [Parameter (
      Position    = 4,
      Mandatory   = $false,
      HelpMessage = "Pattern to exclude"
    )]
    [String]
    $Exclude = $null
  )
  begin {
    if (-Not (Test-Path -Path $Path)) {
      Write-Host "$Path does not exists."
      exit 1
    }
    $ObjectType = [ordered]@{
      "All"     = "items"
      "File"    = "files"
      "Folder"  = "folders"
    }
  }
  process {
    # Get files
    switch ($Type) {
      "File"    {
        $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude -File
      }
      "Folder"  {
        $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude -Directory
      }
      default   {
        $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude
      }
    }
    # If no files are found, print hints
    if ($Files.Count -eq 0) {
      if ($Filter -ne "*") {
        Write-Host "No $($ObjectType[$Type]) were found in $Path matching the filter ""$Filter""."
      } elseif ($Exclude) {
        Write-Host "No $($ObjectType[$Type]) corresponding to the criterias were found in $Path."
      } else {
        Write-Host "No $($ObjectType[$Type]) were found in $Path."
      }
      # exit 1
    } else {
      return $Files
    }
  }
}

$Path         = Split-Path -Path $MyInvocation.MyCommand.Definition
$RelativePath = "\test"
$AbsolutePath = Join-Path -Path $Path -ChildPath $RelativePath
$Filter       = "*"
$Exclude      = $null

Get-ChildItem -Path $RelativePath -Filter $Filter -Exclude $Exclude -File

Get-ChildItem -Path $AbsolutePath -Filter $Filter -Exclude $Exclude -File

Get-Object -Path $RelativePath -Filter $Filter -Exclude $Exclude -Type "File"

Get-Object -Path $AbsolutePath -Filter $Filter -Exclude $Exclude -Type "File"

Output

PS C:\> .\test.ps1


    Directory: C:\test


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       21/09/2018     18:42              0 text.txt
-a----       21/09/2018     18:42              0 text.txt
No files were found in \test.
No files were found in C:\test.

PS: Any constructive criticism on syntax/best practices is welcome.


Solution

  • This seems to be a known problem as per this issue on the PowerShell GIT repository.

    Solution

    There appear to be a conflict while using -Filter, -Exclude, and -File all at the same time. By removing one or adding -Recurse, the function works as expected.

    1. Get-ChildItem -Path $Path -Filter $Filter -File
    2. Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude -File -Recurse

    Obviously this is not optimal and should be considered as a workaround until the issue is fixed.

    Full working example

    function Get-Object {
      [CmdletBinding ()]
      param (
        [Parameter (
          Position    = 1,
          Mandatory   = $true,
          HelpMessage = "Path to the items"
        )]
        [String]
        $Path,
        [Parameter (
          Position    = 2,
          Mandatory   = $false,
          HelpMessage = "Type of object"
        )]
        [ValidateSet ("All", "File", "Folder")]
        [String]
        $Type = "All",
        [Parameter (
          Position    = 3,
          Mandatory   = $false,
          HelpMessage = "Filter to apply"
        )]
        [String]
        $Filter = "*",
        [Parameter (
          Position    = 4,
          Mandatory   = $false,
          HelpMessage = "Pattern to exclude"
        )]
        [String]
        $Exclude = $null
      )
      begin {
        if (-Not (Test-Path -Path $Path)) {
          Write-Host "$Path does not exists."
          exit 1
        }
        $ObjectType = [ordered]@{
          "All"     = "items"
          "File"    = "files"
          "Folder"  = "folders"
        }
      }
      process {
        # Get files
        switch ($Type) {
          "File"    {
            $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude -File -Recurse
          }
          "Folder"  {
            $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude -Directory
          }
          default   {
            $Files = Get-ChildItem -Path $Path -Filter $Filter -Exclude $Exclude
          }
        }
        # If no files are found, print hints
        if ($Files.Count -eq 0) {
          if ($Filter -ne "*") {
            Write-Host "No $($ObjectType[$Type]) were found in $Path matching the filter ""$Filter""."
          } elseif ($Exclude) {
            Write-Host "No $($ObjectType[$Type]) corresponding to the criterias were found in $Path."
          } else {
            Write-Host "No $($ObjectType[$Type]) were found in $Path."
          }
          # exit 1
        } else {
          return $Files
        }
      }
    }
    
    $Path         = Split-Path -Path $MyInvocation.MyCommand.Definition
    $RelativePath = "\test"
    $AbsolutePath = Join-Path -Path $Path -ChildPath $RelativePath
    $Filter       = "*"
    $Exclude      = $null
    
    Get-ChildItem -Path $RelativePath -Filter $Filter -Exclude $Exclude -File
    
    Get-ChildItem -Path $AbsolutePath -Filter $Filter -Exclude $Exclude -File
    
    Get-Object -Path $RelativePath -Filter $Filter -Exclude $Exclude -Type "File"
    
    Get-Object -Path $AbsolutePath -Filter $Filter -Exclude $Exclude -Type "File"
    

    Output

    PS C:\> .\test.ps1
    
    
        Directory: C:\test
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----       21/09/2018     18:42              0 text.txt
    -a----       21/09/2018     18:42              0 text.txt
    -a----       21/09/2018     18:42              0 text.txt
    -a----       21/09/2018     18:42              0 text.txt