1

In Powershell v6, the split-path has the -Extension parameter to access the extension of a file name, for example:

$pathToResume = "R:\Work\cover.letter_resume_base.odt"
$extension = Split-Path -Extension $pathtoResume
Write-Output $extension # Will print .odt

However, Powershell 3 doesn't provide the -Extension parameter, but I came up with this solution:

# Notice the period between cover and letter
$pathToResume = "R:\Work\cover.letter_resume_base.odt"
$pathToResumeLeaf = Split-Path -Leaf $pathToResume
$pathToResumeLeafArray = $pathToResumeLeaf.Split(".")

$fileExtension = $pathToResumeLeafArray | Select-Object -Last 1
Write-Output $fileExtension # Will print odt

I still get the file extension, but without the period. No matter how many periods are in the filename or the length of the array, I will get the same output.

I can't think of any situation where the period is required. If I wanted to print the period with the extension, I can easily add it when I use Write-Output or [string]::format()

Is Select-Object as I've shown above a viable solution when -Extension is unavailable?

  • if all you want is the last item in an array, the faster & cleaner method is `$Array[-1]` to access the last item in the array. ///// if all you want is the extension from a fileinfo object, use `$FileInfoItem.Extension.Trim('.')` ///// if all you want is the extension part of a filename string, use `$FullFileNameString.Split('.')[-1]`. ///// all of the above will give the `Ext` as a result. – Lee_Dailey Mar 30 '19 at 17:24
  • @Lee_Dailey - Is there anything wrong with `Select-Object`? Its the excepted answer [here](https://stackoverflow.com/questions/18018892/pick-last-item-from-list-in-powershell), and it works for me. –  Mar 30 '19 at 17:57
  • 1
    Should be simpler: `$fileExtension = (Get-Item $pathToResume).Extension` –  Mar 30 '19 at 18:02
  • @LotPings - I like your approach. Is there something wrong with `Select-Object` in this case? –  Mar 30 '19 at 18:04
  • 1
    No, just an additional pipe, to get the last element of an array this is also simpler: `$fileExtension = "R:\Work\cover.letter_resume_base.odt".Split('.')[-1] ` –  Mar 30 '19 at 18:08
  • Or to keep the dot in the extension with RegEx and a lookahead: `$fileExtension = ("R:\Work\cover.letter_resume_base.odt" -Split '(?=\.)')[-1]` –  Mar 30 '19 at 18:15
  • @LotPings - doesn't `.Extension` _keep_ the dot? on my win7ps5.1 box, it gives `.Txt` for a text file. – Lee_Dailey Mar 30 '19 at 18:37
  • @Lee_Dailey yes, but the `.split('.')` removes it. –  Mar 30 '19 at 18:39
  • 1
    @nsonline - the problem with Select-Object in this case it two-fold. [1] it requires sending things across the pipeline ... and that is needlessly slow. not much for ONE such, but it mounts up ... and is simply inefficient. [2] the cmdlet is very capable and very generalized ... and that means it does a GREAT DEAL more work than is obvious. the solution i showed is simpler, shorter to type and faster. – Lee_Dailey Mar 30 '19 at 18:40
  • @LotPings - blast![*blush*] i managed to miss that ...sorry for the comment noise. – Lee_Dailey Mar 30 '19 at 18:41
  • 1
    Or use .Net `$extension = [System.IO.Path]::GetExtension($pathToResume)`. Also keeps the dot, but unlike `Get-Item` the path does not need to exist. [GetExtension](https://learn.microsoft.com/en-us/dotnet/api/system.io.path.getextension?view=netframework-4.7.2) – Theo Mar 30 '19 at 21:14
  • @GetExtension @Lee_Dailey @LotPings - For my particular situation the period before the extension doesn't matter, so `.odt` or `odt` is fine. Is it a good practice to keep the period? Are there situations where the period matters? –  Mar 31 '19 at 13:05
  • @nsonline - since it is part of the standard result, i keep it. that simplifies the code ... and doing unneeded work is ... unneeded. [*grin*] – Lee_Dailey Mar 31 '19 at 18:29
  • @Lee_Dailey, @LotPings - Is substring okay as well? `$filename = "R:\Work\cover.letter_resume_base.odt".Substring($filename.Length - 4)`. I'm aware it creates a new string representing the substring, is it inefficient? –  Apr 02 '19 at 14:36
  • @nsonline - the `.SubString()` method is slower than regex stuff, and slower than using `.Extension`. you can test it with `Measure-Command`. however, it is not _vastly slower_ ... so use what fits your way of thinking about the problem. my personal take on getting the extension of a file name ... is to use the code that is built for that - the `[FileInfo]` of a file has that nifty-keano `.Extension` property ... [*grin*] – Lee_Dailey Apr 02 '19 at 17:43

1 Answers1

0

With a path string as the input, a concise and performant - though a bit obscure - PowerShell-native solution is to use the
-replace operator to extract the extension using a regex:

PS> "R:\Work\cover.letter_resume_base.odt" -replace '^.+(\.[^.]+)$', '$1'
.odt

Note: This solution only works reliably if the file path has an extension.

Alternatively, use the .NET framework directly, as Theo suggests:

PS> [IO.Path]::GetExtension("R:\Work\cover.letter_resume_base.odt")
.odt

You can also cast to [System.IO.FileInfo] and access the .Extension property:

PS> ([IO.FileInfo] "R:\Work\cover.letter_resume_base.odt").Extension
.odt

The above is a faster and more flexible alternative to LotPings' Get-Item suggestion, which, as Theo points, requires that the file exist:

# Works, but file must exist.
PS> (Get-Item "R:\Work\cover.letter_resume_base.odt").Extension
.odt

Is Select-Object, as shown above, a viable solution when -Extension is unavailable?

Yes, it is, but it is verbose and slow (though performance may not matter in this case), because using the pipeline and cmdlets invariably introduces overhead compared to using expressions involving operators.

LotPings points out that simply indexing with [-1] into the results of the .Split() call to extract the last token is both more concise and faster.

Also, consider routinely using PowerShell's -split operator instead of the [string] type's .Split() method:

PS> '.' + ("R:\Work\cover.letter_resume_base.odt" -split '\.')[-1]
.odt

As for whether or not to retain the initial . as part of the extension:

Either way, works, as long as you make sure the . is there when you synthesize a filename, but retaining the . has two advantages:

  • looking at the value makes it obvious that you're dealing with an extension

  • you can directly append the value to a file's base name (e,g, $baseName + $ext), which also works in the case when there's no extension (if $ext happens to contain the empty string).

mklement0
  • 382,024
  • 64
  • 607
  • 775