11
votes

OK, so I feel like this must be a bug in PowerShell, but I wanted to see if you guys think this sounds broken. It's quite an easy thing to reproduce, but I can see why it might not be a particularly common use case. The steps I've put below aren't actually what my script was doing, I was actually calculating sizes of subfolders - I've just condensed it down to the simplest possible scenario that shows my problem.

I have only tried this on PowerShell 5.0.10240.16384, but might soon have the chance to test it on an earlier version [Edit: I have now tested this on PowerShell 2.0, and the bug does not appear in that version - it works as expected]. Just a quick note - I've used gci throughout as an abbreviation for Get-ChildItem. If you didn't already know, this works to actually type into PowerShell too. But the problem exists whatever alias you use.

First, create a folder called Test [123] somewhere handy. In that folder, create a couple of files. Mine are caled Test1.txt and Test2.txt. They don't need to have anything in them.

Next, open a PowerShell session and Set-Location to the parent folder of your new Test [123] folder.

Now, run gci -Filter Test* and you should see something like:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       11/15/2015   3:22 PM                Test [123]

All good, right? Next, try gci -Filter Test* | gci. This makes the output of the first gci the input for the next one, ie shows us the children of each item the first gci returns. This gives us the following:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       11/14/2015  10:21 PM              0 Test1.txt
-a----       11/15/2015   3:55 PM              0 Test2.txt

Again, all good - exactly as expected. This is all the files from our Test [123] folder.

Now try this: gci -Filter Test* | gci -Recurse. All we've changed is that the second call to gci is now recursive, so it should show us all files including subfolders. We don't have any subfolders, so we're expecting the same results, right?

Wrong. We don't get any output at all. Seems very odd that all we've done is added -Recurse and now we get different output. I think that adding -Recurse should never render less output, only ever more.

Here's the next weird thing. Now try gci -Filter Test* | gci -Recurse -Name. This is the same as before except with the -Name parameter added. This is simply saying that we want the same output, except we only want the filenames and not the full information about each item. So we're probably expecting nothing back. But that's not what we get, we get this:

Test1.txt
Test2.txt

This has got to be broken, right? Firstly -Recurse should never reduce the amount of output, and secondly asking for the output in a different format should not change the amount of output we get.

This only happens when there are square brackets in the name of the folder. If you create another folder, just called Test, and run all the commands above again, you will always see the files from the Test folder.

My research lead me to the -LiteralPath parameter; however, rephrasing the above command as gci -Filter Test* | foreach { gci -Recurse -LiteralPath $_ }, so as to use that parameter, still returns no output, and again, adding the -Name parameter starts the files being returned again.

I've managed to do a workaround for this, using the -Name parameter and then combining it with the path of the folder I'm searching, then passing that into Get-Item, but that's made the code longer and uglier than it needs to be.

So my question is, have I made a mistake or misunderstood something? Or is this a bug I should report?

2
Use -Include * as workaround.user4003407
Wow, that works! Thanks!Richard Irons
@RichardIrons Seems to be related to how the -Recurse implementation handles the wildcard characters ([ and ]) in the Name. If you escape them manually, it works as wellMathias R. Jessen
@MathiasR.Jessen Why [Regex]::Replace instead of [System.Management.Automation.WildcardPattern]::Escape?user4003407
While I do love PowerShell, Get-ChildItem cmdlet is a complete mess. It's one thing in PS, that never produces consistent output.beatcracker

2 Answers

4
votes

TL;DR: Use -LiteralPath for the folder when there might be unusual characters (inc. square brackets) in the names.


I wasn't able to reproduce this as per the OP in PowerShell v5.1.17134.590.

However, I was able to reproduce something similar, using the following command to attempt to list files in a folder I suspected was empty, in order to delete it. In actual fact, this folder had 12 .mp3 files in it:

[PS]> gci '.\Music\Artist - Name\Album Name [Disc 1]\'

[PS]>

Adding the -Recurse switch caused the cmdlet to return an error rather than the (misleading) empty response shown above. I tested the same command with the -Include * option, and still there were no results, however this time there was no error when combined with the -Recurse switch.

-LiteralPath

The thing that worked best for me was to specify the path with the -LiteralPath parameter:

[PS]> gci -LiteralPath '.\Music\Artist - Name\Album Name [Disc 1]\'

Escaping

To cover some other possible cases, you might want to try to escape the square brackets. Using the suggestion from @PetSerAl in the comments on the OP you could try something like this:

[PS]> [System.Management.Automation.WildcardPattern]::Escape('.\Music\Artist - Name\Album Name [Disc 1]\')

.\Music\Artist - Name\Album Name `[Disc 1`]\

Sadly, this didn't work straight away. I found that I needed to escape twice, and then the following command gave the correct directory listing:

[PS]> gci '.\Music\Artist - Name\Album Name ``[Disc 1``]\'

See the following Q&A for more about square brackets & escaping:

6
votes

It looks like a bug reported here or a very similar issue.