一种在PowerShell中检查路径是否存在的更好方法


122

我只是不喜欢以下语法:

if (Test-Path $path) { ... }

if (-not (Test-Path $path)) { ... }
if (!(Test-Path $path)) { ... }

特别是在检查“不存在”这种常见用法时,括号太多,而且可读性很差。有什么更好的方法可以做到这一点?

更新:我目前的解决方案是使用别名exist,并not-exist为解释在这里

PowerShell存储库中的相关问题:https : //github.com/PowerShell/PowerShell/issues/1970


2
您可以使用try{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris,

Answers:


130

如果只希望使用cmdlet语法的替代方法(特别是用于文件),请使用File.Exists().NET方法:

if(![System.IO.File]::Exists($path)){
    # file with path $path doesn't exist
}

另一方面,如果您想要的通用否定别名Test-Path,则应按以下步骤进行操作:

# Gather command meta data from the original Cmdlet (in this case, Test-Path)
$TestPathCmd = Get-Command Test-Path
$TestPathCmdMetaData = New-Object System.Management.Automation.CommandMetadata $TestPathCmd

# Use the static ProxyCommand.GetParamBlock method to copy 
# Test-Path's param block and CmdletBinding attribute
$Binding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($TestPathCmdMetaData)
$Params  = [System.Management.Automation.ProxyCommand]::GetParamBlock($TestPathCmdMetaData)

# Create wrapper for the command that proxies the parameters to Test-Path 
# using @PSBoundParameters, and negates any output with -not
$WrappedCommand = { 
    try { -not (Test-Path @PSBoundParameters) } catch { throw $_ }
}

# define your new function using the details above
$Function:notexists = '{0}param({1}) {2}' -f $Binding,$Params,$WrappedCommand

notexists现在的行为正好一样Test-Path,但总是返回相反的结果:

PS C:\> Test-Path -Path "C:\Windows"
True
PS C:\> notexists -Path "C:\Windows"
False
PS C:\> notexists "C:\Windows" # positional parameter binding exactly like Test-Path
False

正如你已经展示了自己,相反是很容易的,只是别名existsTest-Path

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True

1
如果$path是“特殊”的,例如在Powershell提供程序上(请考虑HKLM:\ SOFTWARE \ ...),那么它将失败。
伊里斯(Eris)

4
@Eris问题专门要求检查文件是否存在
Mathias R. Jessen

1
当然,动态创建新的cmdlet很简单。几乎像别名一样难以维护,但仍然非常整洁:)
Eris

真好!我认为PS应该为此添加本机支持。
orad 2015年

4
@orad我严重怀疑您是否会让他们这样做。“太多括号”是一个非常主观的推理,并没有真正偏离语言设计/规范的优点。FWIW,如果您真的非常讨厌括号,我也同意@briantist提出的if / else构造作为更好的选择:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen

38

您发布的别名解决方案很聪明,但是我反对在脚本中使用别名,因为同样的原因,我不喜欢在脚本中使用任何别名。它往往会损害可读性。

如果这是您要添加到配置文件中的内容,以便您可以键入快速命令或将其用作外壳,那么我可以认为这很有意义。

您可以考虑管道:

if ($path | Test-Path) { ... }
if (-not ($path | Test-Path)) { ... }
if (!($path | Test-Path)) { ... }

另外,对于否定方法,如果适合您的代码,则可以对其进行肯定检查,然后else对否定方法使用:

if (Test-Path $path) {
    throw "File already exists."
} else {
   # The thing you really wanted to do.
}

1
我喜欢这里的管道,但是您建议的负片检查在不带括号的情况下是不正确的,否则总会得出False。您需要这样做if (-not ($path | Test-Path)) { ... }
orad 2015年

1
@orad你是对的!实际上,在这种情况下,这对管道系统不利。当它确实失败时,我没有抛出异常,这使我陷入一种错误的安全感。以原始方式调用它会引发异常,从而更容易发现问题。
briantist

10

添加以下别名。我认为默认情况下应在PowerShell中提供这些功能:

function not-exist { -not (Test-Path $args) }
Set-Alias !exist not-exist -Option "Constant, AllScope"
Set-Alias exist Test-Path -Option "Constant, AllScope"

这样,条件语句将变为:

if (exist $path) { ... }

if (not-exist $path)) { ... }
if (!exist $path)) { ... }

4
如果希望PowerShell团队添加“现有”别名,则应通过Microsoft Connect提交功能请求
Mathias R. Jessen 2015年

1
即使我自己回答了问题,我也接受@ mathias-r-jessen的回答,因为它可以更好地处理参数。
orad 2015年

2

另一个选择是使用IO.FileInfo,它为您提供了许多文件信息,仅使用此类型即可使工作更轻松:

PS > mkdir C:\Temp
PS > dir C:\Temp\
PS > [IO.FileInfo] $foo = 'C:\Temp\foo.txt'
PS > $foo.Exists
False
PS > New-TemporaryFile | Move-Item -Destination C:\Temp\foo.txt
PS > $foo.Refresh()
PS > $foo.Exists
True
PS > $foo | Select-Object *


Mode              : -a----
VersionInfo       : File:             C:\Temp\foo.txt
                    InternalName:
                    OriginalFilename:
                    FileVersion:
                    FileDescription:
                    Product:
                    ProductVersion:
                    Debug:            False
                    Patched:          False
                    PreRelease:       False
                    PrivateBuild:     False
                    SpecialBuild:     False
                    Language:

BaseName          : foo
Target            : {}
LinkType          :
Length            : 0
DirectoryName     : C:\Temp
Directory         : C:\Temp
IsReadOnly        : False
FullName          : C:\Temp\foo.txt
Extension         : .txt
Name              : foo.txt
Exists            : True
CreationTime      : 2/27/2019 8:57:33 AM
CreationTimeUtc   : 2/27/2019 1:57:33 PM
LastAccessTime    : 2/27/2019 8:57:33 AM
LastAccessTimeUtc : 2/27/2019 1:57:33 PM
LastWriteTime     : 2/27/2019 8:57:33 AM
LastWriteTimeUtc  : 2/27/2019 1:57:33 PM
Attributes        : Archive

在我的博客上有更多详细信息。


1

要检查目录是否存在路径,请使用以下命令:

$pathToDirectory = "c:\program files\blahblah\"
if (![System.IO.Directory]::Exists($pathToDirectory))
{
 mkdir $path1
}

要检查文件路径是否存在,请使用@Mathias建议的内容:

[System.IO.File]::Exists($pathToAFile)

0

这是我的powershell新手方法

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.