Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
996 views
in Technique[技术] by (71.8m points)

powershell - Specifying *.xls filter in Get-ChildItem also returns *.xlsx results

I have a folder that contains both .xls, .xlsx and .xlsm files, and would like to filter just the .xls files.

Why is the following line not working as I'd expect it to? I see .xls, .xlsx and .xlsm results.

Get-ChildItem $(Get-Location) -Filter *.xls | ForEach-Object { $_.Extension }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The -Filter parameter's wildcard matching is not performed by PowerShell, it is passed through to the filesystem provider and ultimately the Windows API. The matching performed there is burdened with many legacy behaviors and quirks, including the one you saw:

  • In Windows PowerShell, -Filter *.xls effectively behaves like -Filter *.xls*. Therefore, -Filter *.xls matches both foo.xls and foo.xlsx, for instance; this happens, because the 8.3 (short) file names are also being matched behind the scenes; for instance, foo.xlsx's 8.3 file name looks something like FOO~1.XLS; note the truncation (and capitalization) of .xlsx to .XLS.

  • While the short-name matching behavior no longer occurs in PowerShell [Core] v6+, fortunately, other legacy quirks persist[1], as does the most notable difference (which won't go away): only PowerShell wildcard expressions (see about_Wildcards) support character ranges / sets via [...] (e.g., [a-z]) - they're not supported with -Filter.

  • Use of the -Filter parameter is in general still preferable to -Path / -Include (see below) due to its superior performance (filtering happens at the source, instead of after the fact in PowerShell).

The workaround is to use the -Path parameter in order to use PowerShell's wildcard matching:

Get-ChildItem -Path (Join-Path (Get-Location) *.xls) | ForEach-Object { $_.Extension }

# Or, more simply
Get-ChildItem -Path $PWD/*.xls | ForEach-Object Extension

Note: With -Recurse you'd use the -Include parameter instead.


[1] Notable other quirks:

  • Multiple consecutive ? wildcards can match names with fewer characters.

    • E.g., Get-ChildItem -Filter ??.txt matches aa.txt and unexpectedly also a.txt
    • Note: This was temporarily fixed in PowerShell Core 6.x (as of 6.2.3), but the behavior is back as of PowerShell Core 7.0.0-rc.2
  • Pattern *. matches extension-less file and directory names.

    • E.g., Get-ChildItem -File -Filter *. finds all files (-File) whose names do not have an extension (e.g., file); this quirk can actually be useful, in that it is the simplest and best-performing way to locate extension-less files (-Path *. does not work, because it looks for a file name literally ending in a .).

    • Note: This was temporarily changed in PowerShell Core 6.x (as of 6.2.3), but the behavior is back as of PowerShell Core 7.0.

  • Conversely, *.* includes extension-less file and directory names as well.

See this excellent answer by Zenexer for the backstory and the gory details.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...