Is it possible to filter using Get-ChildItem -Name

2019-07-27 09:38发布

问题:

In one of the questions I answered recently I found interesting answer which shouldn't be working but still was. The question was about how to find specific folder recursively by its name and cd to it.

The answer proposed by A guest who's called Redd was:

Get-ChildItem -Path .\ -Name Folder -Recurse -Depth 10

As per the documentation of Get-ChildItem, -Name parameter is supposed to be SwitchParameter type and is responsible for returning only name (System.String), instead of System.Object.

How it's possible that the solution still works?


MCVE:

# cd C:\SO\56628221
mkdir test, test1, test2, test3
mkdir .\test2\folder
Get-ChildItem -Path .\ -Name Folder -Recurse -Depth 10

Current output:

test2\folder

Expected output:

Get-ChildItem : A positional parameter cannot be found that accepts argument 'Folder'.

What have I tried?

  1. First I checked that -Path is the only positional parameter. Apparently it is:

All the other params have Position: Named.

  1. Then I tried to switch the arguments to something like this:
Get-ChildItem -Path .\ Folder -Name -Recurse -Depth 10

It was still working, so that was clear indication that what I'm passing to the cmdlet is not the value for -Name.

  1. Last thing I supposed was that I just send array of strings to -Path. I tried to do this explicitely:
[string[]]$a = '.\','Folder'
$a.GetType()
Get-ChildItem -Path $a -Name -Recurse -Depth 10

# Output:
PS C:\SO\56628221> $a.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array


PS C:\SO\56628221> Get-ChildItem -Path $a -Name -Recurse -Depth 10
test
test1
test2
test3
test2\folder
Get-ChildItem : Cannot find path 'C:\SO\56628221\Folder' because it does not exist.
At line:1 char:1
+ Get-ChildItem -Path $a -Name -Recurse -Depth 10
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\SO\56628221\Folder:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

回答1:

tl;dr:

Apparently there's an incorrect information in current version of Get-ChildItem documentation, stating that -Filter is no longer positional.

The above is no longer true, it's been fixed in this PR.


Long answer:

In fact, the value 'Folder' is being passed to -Filter parameter. Even though PowerShell 6 documentation says opposite, -Filter is a positional parameter. By mistake, that change was introduced in PowerShell v6+ while PowerShell 5.1 help article for Get-ChildItem is still correct.

The cmdlet you run:

Get-ChildItem -Path .\ -Name Folder -Recurse -Depth 10

is effectively:

Get-ChildItem -Path ".\" -Name -Filter "Folder" -Recurse -Depth 10

Even though -Filter parameter in Get-ChildItem might be tricky in usage, in that case it works perfectly and the filter is applied to only show items named 'Folder'. As that invocation doesn't specify -File or -Directory, if you run:

# Create new file named 'Folder'
New-Item Folder

and then run the cmdlet once again, it'd return both file and folder which was created:

PS C:\SO\56628221> Get-ChildItem -Path .\ -Name Folder -Recurse -Depth 10
Folder
test2\folder

The output is exactly the same if you explicitely use -Filter:

PS C:\SO\56628221> Get-ChildItem -Path .\ -Name -Filter Folder -Recurse -Depth 10
Folder
test2\folder