157

I have a series of strings which are full paths to files. I'd like to save just the filename, without the file extension and the leading path. So from this:

c:\temp\myfile.txt

to

myfile

I'm not actually iterating through a directory, in which case something like PowerShell's basename property could be used, but rather I'm dealing with strings alone.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
larryq
  • 15,713
  • 38
  • 121
  • 190
  • 9
    many answers are not taking in account second part of the question. When Get-Item, Get-ChildItem, or their aliases ls, dir, gi, gci are used, the file from the tested string **must exist**. When we are checking _a series of string_ and not _iterating through a directory_, it must be assumed those files doesn't need to exist on computer where this script will be run. – papo Jul 26 '17 at 06:21

14 Answers14

180

Way easier than I thought to address the issue of displaying the full path, directory, file name or file extension.

                                           ## Output:
$PSCommandPath                             ## C:\Users\user\Documents\code\ps\test.ps1
(Get-Item $PSCommandPath ).Extension       ## .ps1
(Get-Item $PSCommandPath ).Basename        ## test
(Get-Item $PSCommandPath ).Name            ## test.ps1
(Get-Item $PSCommandPath ).DirectoryName   ## C:\Users\user\Documents\code\ps
(Get-Item $PSCommandPath ).FullName        ## C:\Users\user\Documents\code\ps\test.ps1

$ConfigINI = (Get-Item $PSCommandPath ).DirectoryName+"\"+(Get-Item $PSCommandPath ).BaseName+".ini"

$ConfigINI                                 ## C:\Users\user\Documents\code\ps\test.ini

Other forms:

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
split-path -parent $PSCommandPath
Split-Path $script:MyInvocation.MyCommand.Path
split-path -parent $MyInvocation.MyCommand.Definition
[io.path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
Tony
  • 2,658
  • 2
  • 31
  • 46
Leonardo
  • 1,819
  • 1
  • 9
  • 3
  • 38
    It would be nice if beside each example in the top code snippet you showed exactly what text would be returned. – deadlydog Nov 12 '17 at 17:36
  • Example where I don't know the .csr file name, but I know a file exists: `$csr = Get-ChildItem -Path "$($domain.FullName)/*.csr"` then `Write-Host "fileName: $($csr.Basename)"` – Scott Pelak Oct 24 '18 at 12:54
  • 4
    @Leonardo `Get-Item` requires that the file exists, otherwise it throws an error – SebMa Nov 18 '20 at 14:34
  • 1
    @SebMa `$PSCommandPath` returns information of the currently running script, which should be in a saved state before execution. `Get-Item` in this case should always return a valid value. – Tony Feb 04 '22 at 07:44
155

There's a handy .NET method for that:

C:\PS> [io.path]::GetFileNameWithoutExtension("c:\temp\myfile.txt")
myfile
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 1
    This method name is misleading... it seems to get the file name without the extension *or the file path*. That's useful if it's what you want, but a deal-breaker if you only want to remove the extension... which someone finding this method would be led to believe. – TylerH Apr 22 '21 at 20:34
  • 4
    These days, on PowerShell v7, I'd simply use `Split-Path C:\temp\myfile.txt -LeafBase`. – Keith Hill Apr 23 '21 at 20:10
  • 2
    @KeithHill thanks! -LeafBase was introduced in powershell v6 for anyone else curious – Crescent Fresh May 03 '21 at 13:00
  • 1
    @TylerH I don't find this method name misleading. Rather, I find it to be quite consistent with [io.path]::GetFileName. I would say that GetFileName is quite clear in its intent and GetFileNameWithoutExtension should do the same thing--without the extension. – stritch000 May 18 '21 at 21:08
71

Inspired by an answer of @walid2mi:

(Get-Item 'c:\temp\myfile.txt').Basename

Please note: this only works if the given file really exists.

CodeFox
  • 3,321
  • 1
  • 29
  • 41
33

or

([io.fileinfo]"c:\temp\myfile.txt").basename

or

"c:\temp\myfile.txt".split('\.')[-2]
walid2mi
  • 2,704
  • 15
  • 15
  • 11
    The second example doesn't work too well with something like - "C:\Downloads\ReSharperSetup.7.0.97.60.msi".split('\.')[-2] – Keith Hill Sep 20 '12 at 17:15
  • @KeithHill the professional filename does not have dots other than the dot to the extension I gather. But this is to be discussed. If one is generous, one would assume an extension of three characters, so I would `$FileNameWoExt = $FileName.Substring(0, $FileName.Length -4)` – Timo Dec 07 '20 at 19:32
  • 2
    "one would assume an extension of three characters" is not a good assumption. There are plenty of extensions that use more or less than three characters e.g.: .psd1, .psm1, .json, .docx, .xslx, .pptx, .appx, .appbundle, .cs, .fs, .c, .h, .py, etc. – Keith Hill Dec 08 '20 at 19:55
28

you can use basename property

PS II> ls *.ps1 | select basename
walid2mi
  • 2,704
  • 15
  • 15
17

Starting with PowerShell 6, you get the filename without extension like so:

split-path c:\temp\myfile.txt -leafBase
René Nyffenegger
  • 39,402
  • 33
  • 158
  • 293
8

@Keith,

here another option:

PS II> $f="C:\Downloads\ReSharperSetup.7.0.97.60.msi"

PS II> $f.split('\')[-1] -replace '\.\w+$'

PS II> $f.Substring(0,$f.LastIndexOf('.')).split('\')[-1]
jpaugh
  • 6,634
  • 4
  • 38
  • 90
walid2mi
  • 2,704
  • 15
  • 15
8

Expanding on René Nyffenegger's answer, for those who do not have access to PowerShell version 6.x, we use Split Path, which doesn't test for file existence:

Split-Path "C:\Folder\SubFolder\myfile.txt" -Leaf

This returns "myfile.txt". If we know that the file name doesn't have periods in it, we can split the string and take the first part:

(Split-Path "C:\Folder\SubFolder\myfile.txt" -Leaf).Split('.') | Select -First 1

or

(Split-Path "C:\Folder\SubFolder\myfile.txt" -Leaf).Split('.')[0]

This returns "myfile". If the file name might include periods, to be safe, we could use the following:

$FileName = Split-Path "C:\Folder\SubFolder\myfile.txt.config.txt" -Leaf
$Extension = $FileName.Split('.') | Select -Last 1
$FileNameWoExt = $FileName.Substring(0, $FileName.Length - $Extension.Length - 1)

This returns "myfile.txt.config". Here I prefer to use Substring() instead of Replace() because the extension preceded by a period could also be part of the name, as in my example. By using Substring we return the filename without the extension as requested.

Papa Ccompis
  • 545
  • 5
  • 7
  • 1
    Split-Path is so powerful. Acts as [dirname](/q/63669556), [basename](/q/54305545) and [more](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/split-path) - only [not on URLs](/q/53766303). – cachius Apr 28 '22 at 23:01
4

Given any arbitrary path string, various static methods on the System.IO.Path object give the following results.

strTestPath                 = C:\Users\DAG\Documents\Articles_2018\NTFS_File_Times_in_CMD\PathStringInfo.ps1
GetDirectoryName            = C:\Users\DAG\Documents\Articles_2018\NTFS_File_Times_in_CMD
GetFileName                 = PathStringInfo.ps1
GetExtension                = .ps1
GetFileNameWithoutExtension = PathStringInfo

Following is the code that generated the above output.

[console]::Writeline( "strTestPath                 = {0}{1}" ,
                      $strTestPath , [Environment]::NewLine );
[console]::Writeline( "GetDirectoryName            = {0}" ,
                      [IO.Path]::GetDirectoryName( $strTestPath ) );
[console]::Writeline( "GetFileName                 = {0}" ,
                      [IO.Path]::GetFileName( $strTestPath ) );
[console]::Writeline( "GetExtension                = {0}" ,
                      [IO.Path]::GetExtension( $strTestPath ) );
[console]::Writeline( "GetFileNameWithoutExtension = {0}" ,
                      [IO.Path]::GetFileNameWithoutExtension( $strTestPath ) );

Writing and testing the script that generated the above uncovered some quirks about how PowerShell differs from C#, C, C++, the Windows NT command scripting language, and just about everything else with which I have any experience.

WileCau
  • 2,057
  • 1
  • 24
  • 34
David A. Gray
  • 1,039
  • 12
  • 19
3

Here is one without parentheses

[io.fileinfo] 'c:\temp\myfile.txt' | % basename
mklement0
  • 382,024
  • 64
  • 607
  • 775
Zombo
  • 1
  • 62
  • 391
  • 407
3

This script searches in a folder and sub folders and rename files by removing their extension

    Get-ChildItem -Path "C:/" -Recurse -Filter *.wctc |

    Foreach-Object {

      rename-item $_.fullname -newname $_.basename

    }
Stanley De Boer
  • 4,921
  • 1
  • 23
  • 31
2

This can be done by splitting the string a couple of times.

#Path
$Link = "http://some.url/some/path/file.name"

#Split path on "/"
#Results of split will look like this : 
# http:
#
# some.url
# some
# path
# file.name
$Split = $Link.Split("/")

#Count how many Split strings there are
#There are 6 strings that have been split in my example
$SplitCount = $Split.Count

#Select the last string
#Result of this selection : 
# file.name
$FilenameWithExtension = $Split[$SplitCount -1]

#Split filename on "."
#Result of this split : 
# file
# name
$FilenameWithExtensionSplit = $FilenameWithExtension.Split(".")

#Select the first half
#Result of this selection : 
# file
$FilenameWithoutExtension = $FilenameWithExtensionSplit[0]

#The filename without extension is in this variable now
# file
$FilenameWithoutExtension

Here is the code without comments :

$Link = "http://some.url/some/path/file.name"
$Split = $Link.Split("/")
$SplitCount = $Split.Count
$FilenameWithExtension = $Split[$SplitCount -1]
$FilenameWithExtensionSplit = $FilenameWithExtension.Split(".")
$FilenameWithoutExtension = $FilenameWithExtensionSplit[0]
$FilenameWithoutExtension
OutOfThisPlanet
  • 336
  • 3
  • 17
2

The command below will store in a variable all the file in your folder, matchting the extension ".txt":

$allfiles=Get-ChildItem -Path C:\temp\*" -Include *.txt
foreach ($file in $allfiles) {
    Write-Host $file
    Write-Host $file.name
    Write-Host $file.basename
}

$file gives the file with path, name and extension: c:\temp\myfile.txt

$file.name gives file name & extension: myfile.txt

$file.basename gives only filename: myfile

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
1

Here are a couple PowerShell 5.1 one-liner options that put the path at the start of the line.

'c:\temp\myfile.txt' |%{[io.fileinfo]$_ |% basename}

OR

"c:\temp\myfile.txt" | Split-Path -Leaf | %{$_ -replace '\.\w+$'}
Vopel
  • 662
  • 6
  • 11