4

I am trying to run a script that will display files with double extensions, but the problem is that it displays all the files instead of just the ones with the double extension. Is there a way of doing this?

here is what i have:

Get-ChildItem -Path C:\Users\User\Downloads -filter *.txt.* -Recurse

This display all the txt files as well as the ones with an extra .txt (test.txt.txt) So I am trying to just display the double txt extension.

Hope i have explained this properly.

Thanks

mklement0
  • 382,024
  • 64
  • 607
  • 775

3 Answers3

3

Use -Include rather than -Filter, which makes the wildcard pattern work as intended:

Get-ChildItem -Path C:\Users\User\Downloads -Recurse -Include *.txt.*

Note: This matches files that have .txt. anywhere in the filename, including foo.txt.csv or "triple extensions" files such as foo.txt.txt.txt.
*.txt.txt would yield only filenames that end in .txt.txt, which would still include triple-or-more .txt-extension filenames.
Note that *.txt.txt would work with -Filter too.

While the -Filter parameter has one major advantage - it makes the underlying filesystem provider perform filtering at the source which makes it much faster - it has limitations and pitfalls:

  • The wildcard pattern language is more limited than PowerShell's own, with legacy quirks - see this excellent answer for background information.

    • In essence, only * and ? are supported (not also character set/range expressions such as [0-9]), and .* or just . at the end of a pattern match the empty string too (which is what you experienced).
    • In Windows PowerShell (no longer a problem in PowerShell [Core] 6+), matching is unexpectedly also performed against the normally hidden short (8.3) filenames, which means that something like *.xls unexpectedly behaves like *.xls* and also matches .xlsx files, for instance.
  • You can only pass a single wildcard pattern to -Filter, whereas -Include supports multiple.


On a side note, -Include and -Exclude have their quirks too. Here, -Include works as intended due to -Recurse; without -Recurse, you'd have to make your -Path value end in \* to make it work - see GitHub issue #3304.

Alternatively, if you need only a single wildcard pattern, you can get away with directly appending that pattern to the path - C:\Users\User\Downloads\*.txt.* - as demonstrated in Matt's helpful answer.
However, the caveat is that with -Recurse this only works as intended in PSv3 or above;
PSv2, by contrast, applies the pattern only to the immediate children (which was arguably the more sensible behavior).


On a related but distinct note, TessellatingHeckler has discovered an oddity when specifying literal paths that end in . chars. (present since at least PSv2 and still as of PSv5.1):

PS> Get-Item -LiteralPath c:\windows\system32\cmd.exe... | Format-List Name, Extension

Name      : cmd.exe...
Extension : .exe

Note how (a) cmd.exe was still matched, despite the extraneous trailing . chars., and (b) the .Name property reflects those . chars. - even though neither .Extension nor .FullName do.

The issue has been reported here, but since the the cause is the legacy-bound behavior of the underlying GetFileAttributes native Windows API function, this issue will not be resolved in PowerShell.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thanks for the linked question. That is what I could never find. Never even occurred to look here for it. – Matt Feb 23 '17 at 20:34
  • @Matt: Glad to hear it's helpful; it is unfortunate that the [`Get-ChildItem` help topic](https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.management/get-childitem) is so tight-lipped about the subject, with no link to additional information: "The syntax of the filter, including the use of wildcards, depends on the provider." – mklement0 Feb 23 '17 at 20:37
2

Looking for an official documentation as there is nothing concrete on the MSDN for Get-ChildItem on this. mklement0 links to an answer that covers this If you put this into the path you would get the results you are looking for.

Get-ChildItem -Path "C:\Users\User\Downloads\*.txt.*"

That might be slower than -Filter but it would get you what you wanted and does work recursively (in PowerShell v3.0+). That only covers you for txt files though. If you wanted to file all doubled/repeated extension files you could post process the Get-ChildItem with a where clause.

Get-ChildItem -Path "C:\Users\User\Downloads" -Recurse -File | 
    Where-Object{$_.Extension -and $_.BaseName -match "$([regex]::Escape($_.Extension))$"} 

The clause matches for files that

  1. Have an extension and
  2. The names (without extension) have that same extension on the end. $_.Extension will return ".txt" for example. -match supports regex so we need to escape that period to ensure no false positives.

The -File parameter is definitely part of PS v3.0. That will return all files that have a double extension at the end of their fullnames. The trailing $ is not interpreted by PowerShell as anything special since it does not have any characters following it that would be confused as a variable. You could escape `$, but that would be only to serve as a reminder of what the dollar sign is for. The presence of the backtick here does not change the outcome in this instance.

Community
  • 1
  • 1
Matt
  • 45,022
  • 8
  • 78
  • 119
0

Files don't really have double extensions, they have one extension and you have files with dots in the name. e.g. Appartment 1.04 in block ten - building report.txt will come out in your report as a double extension called .04 in block ten - building report.txt.

So:

Get-ChildItem -Path C:\Users\User\Downloads -Recurse | Where {$_.Extension -eq '.txt' -and $_.BaseName -match '\.' }

Will get all files and filter .txt extensions and names with dots in them.

TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87