Powershell: "get-childitem c:\ -exclude windows" has no output

Created on 21 Jan 2020  路  7Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

get-childitem c:\ -exclude windows

Expected behavior


    Directory: C:\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          10/10/2019  2:17 PM                PerfLogs
d-r--          10/10/2019 10:20 AM                Program Files
d-r--          10/24/2019  3:56 PM                Program Files (x86)
d----           4/18/2019 11:05 AM                temp
d-r--           9/10/2019 10:00 AM                Users

Actual behavior


Environment data

It could be 5 6 or 7.

Name                           Value
----                           -----
PSVersion                      6.2.3
PSEdition                      Core
GitCommitId                    6.2.3
OS                             Microsoft Windows 10.0.16299
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Area-Cmdlets-Management Issue-Bug

All 7 comments

@mklement0 has a very detailed discussion of this behaviour here in a StackOverflow answer but the short version would be:

  • "The implementation of -Include / -Exclude with Get-ChildItem is unintuitive and has pitfalls"
  • The command to do what you want is Get-Item c:\* -Exclude windows (rather than get-childitem).
  • The "get-childitem -exclude option" does filtering on the path you give to it, rather than the childitems it is about to output. You give it a path with a wildcard in it, it resolves the wildcard to a list of possibles, then includes/excludes from that set of wildcarded paths, then looks for childitems in the remaining set and outputs all of them. From this design it can't do what you ask. Get-ChildItem -Path c:\* -Exclude Windows will get you the child items of all the folders in c: excluding the childitems in c:windows, or one level too deep. Compare with get-childitem c:\ -Exclude \ which gets you the child items of c: excluding anything in c: - i.e. nothing.
  • Even though it still seems like this should work for you, the SO answer says "In the special case of path C: (Get-ChildItem -Path C: -Exclude ), where arguably the last path component is the empty string, it seems that any exclusion pattern is considered a match, and no output is ever produced" - this may be a bug, but also may be a breaking change to fix it.

(The help get-childitem -online text is not very clear on explaining this behaviour of -Exclude)

Excluding on the root directory seems to be a special case. On subdirectories it works fine.

get-childitem c:\windows -directory -exclude winsxs

By the way, specifying the path this way gives an error message:

get-childitem Microsoft.PowerShell.Core\FileSystem::C:\ -exclude windows

get-childitem : Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ get-childitem Microsoft.PowerShell.Core\FileSystem::C:\ -exclude wind ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], PSArgumentException
    + FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.GetChildItemCommand

You'll get something similar in osx:

get-childitem / -directory -exclude var

get-childitem : Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ get-childitem / -directory -exclude var
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.GetChildItemCommand

By the way, I'm having trouble editing these posts in any browser.

This seems to be a workaround for ps 6 & 7:

get-item c:\ | get-childitem -exclude *.txt

The simplest workaround is still the one quoted by @HumanEquivalentUnit above:

get-item c:\* -exclude *.txt

Thanks for the summary, @HumanEquivalentUnit - small correction:

does filtering _on the path you give to it_, rather than the child items it is about to output.

It applies the filter on the given path _first_, and then to the child items - and that first application may prevent child items from getting considered at all. Situationally, it still _happens to_ work as expected, as in @jszabo98's get-childitem c:\windows -directory -exclude winsxs example: the -Exclude pattern doesn't exclude c:\windows itself, so the child items _are_ considered, and WinSxS is properly excluded.

This counter-intuitive, but seemingly by-(unfortunate)-design behavior has previously been reported in #3304 (I previously got the above distinction wrong there as well, but have fixed it now).

I've made that clearer in the SO answer, and I've added a link to this issue to highlight the outright _bug_ with _root_ paths that @jszabo98 discovered here (which the SO answer described inadequately - fixed now).

To summarize the bug:

Targeting a _root_ directory with either Get-ChildItem or Get-Item in combination with either -Include or -Exclude - irrespective of the specific patterns passed - is broken, in the following scenarios:

  • On Windows _without_ a provider-qualified path: the command is unexpectedly a _quiet no-op_

  • On Unix-like platforms _without_ a provider-qualified and on Windows _with_ a provider-qualified path: the command breaks with statement-terminating error cannot process argument because the value of argument "path" is not valid.

Curiously, the behavior is _not_ broken _with_ a provider-qualified path on _Unix_.

Here are Pester tests:

Describe "Get-[Child]Item -Include / -Exclude tests" {

  BeforeAll {

    $rootPaths = '/', '\' + ($IsWindows ? 'c:\', 'c:/' : @())
    $qualifiers = '', 'Microsoft.PowerShell.Core\FileSystem::'
    $commands = @(
      { Get-ChildItem $rootPath -Exclude NothingToExclude }
      { Get-ChildItem $rootPath -Include * }
      { Get-Item $rootPath -Exclude NothingToExclude }
      { Get-Item $rootPath -Include * }
    )
    $testCases = foreach ($rootPath in $rootPaths) {
      foreach ($qualifier in $qualifiers) {
        foreach ($command in $commands) {
          @{
            Command = $command
            RootPath = $qualifier + $rootPath
          }
        }
      }
    }

  }

  It "Command { <Command> } works with root path '<RootPath>'" -TestCases $testCases {
    param($Command, $RootPath)

    $(try { & $Command } catch { Write-Warning "{ $Command } with root path '$RootPath' failed unexpectedly: $_" }) | Should -Not -BeNullOrEmpty

  }

}

These tests revealed an unrelated bug on Windows, namely that Get-Item Microsoft.PowerShell.Core\FileSystem::\ and Get-Item Microsoft.PowerShell.Core\FileSystem::/ by themselves don't work (whereas Get-ChildItem does) - see #11660

This might be the cause. The string version of the parent properties returned is an empty string.

get-childitem c:\ | % { "$($_.parent)" }




I don't think that's related, because it is only true in _Windows PowerShell_, and looks like the old stringification problem of System.IO.FileSystemInfo _situationally_ stringifying by file _name_ only, and the file name of a root path is the empty string.

Oh I see, fileinfo's don't even have parent properties.

Was this page helpful?
0 / 5 - 0 ratings