在PowerShell中从文件名中删除路径和扩展名

160

我有一系列字符串,它们是文件的完整路径。我想只保存文件名,不包括文件扩展名和前导路径。因此从这个字符串:

c:\temp\myfile.txt

为了

myfile

实际上我并不是在遍历目录,因此不能像PowerShell的basename属性一样使用。而是只处理字符串。


9
许多答案没有考虑问题的第二部分。当使用Get-Item、Get-ChildItem或它们的别名ls、dir、gi、gci时,必须存在被测试字符串中的文件。当我们检查一系列字符串而不是遍历目录时,必须假定这些文件不需要存在于运行此脚本的计算机上。 - papo
14个回答

189

解决显示完整路径、目录、文件名或文件扩展名问题比我想象中要容易得多。

                                           ## 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

其他形式:

$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)

38
如果在您展示的顶部代码片段中,每个示例旁边都显示返回的确切文本,那就太好了。 - deadlydog
例如,我不知道.csr文件的名称,但我知道该文件存在:$csr = Get-ChildItem -Path "$($domain.FullName)/*.csr" 然后 Write-Host "fileName: $($csr.Basename)" - Scott Pelak
4
Get-Item 需要文件存在,否则会抛出错误。 - SebMa
1
@SebMa $PSCommandPath 返回当前正在运行的脚本的信息,在执行之前应该处于已保存状态。在这种情况下,Get-Item 应该始终返回有效值。 - Tony
终于找到了如何获取运行脚本的名称(不带扩展名)的正确答案:(Get-Item $PSCommandPath ).Basename。谢谢! - undefined

156

有一个方便的 .NET 方法可以做到这一点:

C:\PS> [io.path]::GetFileNameWithoutExtension("c:\temp\myfile.txt")
myfile

1
这个方法的名称有误导性...它似乎获取文件名而不包括扩展名或文件路径。如果这正是你想要的,那很有用,但如果你只想删除扩展名,那就是一个致命缺陷...这会让找到这个方法的人产生误解。 - TylerH
4
现在,在PowerShell v7上,我会简单地使用Split-Path C:\temp\myfile.txt -LeafBase - Keith Hill
2
@KeithHill 谢谢!-LeafBase在PowerShell v6中被引入,如果有其他人感兴趣的话。 - Crescent Fresh
1
@TylerH 我不认为这个方法名有误导性。相反,我觉得它与[io.path] :: GetFileName非常一致。我会说GetFileName在其意图上非常清晰,而GetFileNameWithoutExtension应该做同样的事情--去掉扩展名。 - stritch000

72

受@walid2mi的回答的启发:

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

请注意:此方法仅在所给文件真实存在时有效。


34
或者
([io.fileinfo]"c:\temp\myfile.txt").basename
或者
"c:\temp\myfile.txt".split('\.')[-2]

11
第二个例子对于像“C:\Downloads\ReSharperSetup.7.0.97.60.msi”这样的内容并不太有效,因为它只能提取到倒数第二个点号分隔符后的数字。 - Keith Hill
@KeithHill 专业的文件名除了扩展名中的点号外,不应该有其他点号。但这还需要讨论。如果慷慨一些,我们可以假设扩展名是三个字符,那么我会使用 $FileNameWoExt = $FileName.Substring(0, $FileName.Length -4) 这个命令。 - Timo
2
“人们可能会假设扩展名只有三个字符,”这不是一个好的假设。实际上,有很多扩展名使用的字符数量多于或少于三个,例如:.psd1,.psm1,.json,.docx,.xslx,.pptx,.appx,.appbundle,.cs,.fs,.c,.h,.py等。 - Keith Hill

28

你可以使用 basename 属性

PS II> ls *.ps1 | select basename

8
楼主说:“实际上我并不是在遍历一个目录。” - CB.

18

从PowerShell 6开始,您可以通过以下方式获取不带扩展名的文件名:

split-path c:\temp\myfile.txt -leafBase

3
这在Powershell 6中是正确的。 在标准5.1中没有LeafBase。 - finlaybob
2
谢谢提供信息,我已经相应地更新了答案。什么是bog - René Nyffenegger
2
抱歉 :) 在我来自的英国,“Bog Standard”是一个俚语,用来形容完全普通的事物,即“香草”版本。 - finlaybob

8

@Keith,你好,这里还有一种选择:

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]

8

在扩展René Nyffenegger的回答时,对于那些没有访问PowerShell 6.x版本的人,我们使用Split Path,它不会测试文件是否存在:

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

这将返回“myfile.txt”字符串。如果我们知道文件名中不含有句号,我们可以用分割字符串的方式获取第一部分:
(Split-Path "C:\Folder\SubFolder\myfile.txt" -Leaf).Split('.') | Select -First 1

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

这将返回 "myfile"。 如果文件名可能包含句点,为了安全起见,我们可以使用以下方法:
$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)

这将返回"myfile.txt.config"。 在这里我更喜欢使用Substring()而不是Replace(),因为扩展名前面的句点也可能是名称的一部分,就像我的例子一样。通过使用Substring,我们按要求返回没有扩展名的文件名。

1
Split-Path非常强大。它可以充当dirnamebasename更多的作用,但不适用于URLs - cachius
对于那些无法访问PowerShell 6.x版本的人,我们使用Split Path。[https://stackoverflow.com/a/58628027/641833](René Nyffenegger的回答)表明需要PowerShell 6.0版本。也许你写错了? - undefined

4

针对任意路径字符串,System.IO.Path对象上的各种静态方法提供以下结果。

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

以下是生成上述输出的代码。

[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 ) );

撰写并测试生成上述内容的脚本时,发现了有关PowerShell与C#,C,C ++,Windows NT命令脚本语言以及我熟悉的几乎所有其他语言存在差异的一些怪异问题。

3

这是一个没有括号的例子

[io.fileinfo] 'c:\temp\myfile.txt' | % basename

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接