I was writing a PowerShell script to process some dated files in a folder and I was bemused when I discovered my script was picking up files that didn’t appear to match the very specific filter I had used. If you want to play along at home the following commands will create two empty files with very similar names to the ones that confused me (it was also a backed up file that got picked up by mistake):
> mkdir C:\Temp\Test-GCI
> echo. 2>C:\Temp\Test-GCI\File-20140521.txt
> echo. 2>C:\Temp\Test-GCI\File-20140521-Backup.txt
Hopefully you’ll agree with me that the following attempt to match files in the test folder should only match a single file, right? After all the mask only uses the ? character which matches a single character, unlike * which can match many.
> PowerShell “Get-ChildItem -Path C:\Temp\Test-GCI
-Filter File-????????.txt | select Name”
Name
----
File-20140521.txt
File-20140521-Backup.txt
Eh? That can’t be right. So I started doing some googling and came across some StackOverflow posts like this one which mentions that the -Filter switch behaves differently to the -Like operator. The Get-ChildItem documentation tells you that -Filter is probably more efficient but the semantics are those of the underlying provider, not PowerShell’s. Doing a “dir File-????????.txt” gives the same unexpected result which ties up with the PowerShell documentation.
The solution seems to be to include the file mask in the -Path argument instead of using the separate -Filter switch:
> PowerShell “Get-ChildItem -Path (Join-Path C:\Temp\Test-GCI File-????????.txt) | select Name”
Name
----
File-20140521.txt
OK, problem solved. But what’s curious here is that it doesn’t match what you get if you do “dir C:\Temp\Test-GCI\File-????????.txt” which is an interesting inconsistency that might trip you up if you’re going the other way round (testing with dir and then using the pattern with Get-ChildItem).
If you want to know why the native mask behaves like it does then you need to read Raymond Chen’s 2007 blog post “How did wildcards work in MS-DOS?”.
Yet another reason I prefer F# for this sort of thing ;-)
ReplyDelete