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

ora*_*rad 100 powershell

我只是不喜欢以下语法:

if (Test-Path $path) { ... }
Run Code Online (Sandbox Code Playgroud)

if (-not (Test-Path $path)) { ... }
if (!(Test-Path $path)) { ... }
Run Code Online (Sandbox Code Playgroud)

特别是在检查"不存在"这种常见用途时,括号太多且不易读.有什么更好的方法呢?

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

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

Mat*_*sen 109

如果您只想要替代cmdlet语法,特别是文件,请使用File.Exists().NET方法:

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

另一方面,如果你想要一个通用的否定别名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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True
Run Code Online (Sandbox Code Playgroud)

  • @Eris问题专门要求检查*文件*是否存在 (3认同)
  • @orad我严重怀疑你会让他们这样做."括号太多"是一种非常*主观的推理,并不值得偏离语言设计/规范.FWIW,我也同意@briantist提出的if/else结构作为一个更好的选择,如果你真的讨厌括号:`if(Test-Path $ path){} else {#do your thing} (3认同)
  • 当然,动态创建新的 cmdlet 非常简洁。几乎和别名一样难以维护,但仍然非常简洁:) (2认同)

bri*_*ist 30

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

如果这是您要添加到配置文件中的内容,那么您可以键入快速命令或将其用作shell,那么我可以看到它有意义.

您可能会考虑使用管道:

if ($path | Test-Path) { ... }
if (-not ($path | Test-Path)) { ... }
if (!($path | Test-Path)) { ... }
Run Code Online (Sandbox Code Playgroud)

或者,对于否定方法,如果适合您的代码,您可以将其作为肯定检查,然后else用于否定:

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


小智 8

这是我的 PowerShell 新手的做法

if (Test-Path ".\Desktop\checkfile.txt") {
    Write-Host "Yay"
} 
else {
    Write-Host "Damn it"
}
Run Code Online (Sandbox Code Playgroud)


ora*_*rad 7

添加以下别名.我认为这些应该默认在PowerShell中提供:

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

这样,条件语句将变为:

if (exist $path) { ... }
Run Code Online (Sandbox Code Playgroud)

if (not-exist $path)) { ... }
if (!exist $path)) { ... }
Run Code Online (Sandbox Code Playgroud)

  • 如果您希望PowerShell团队添加"存在"别名,则应通过Microsoft Connect提交功能请求 (4认同)
  • 好的,这是[在Microsoft Connect上提交此内容](https://connect.microsoft.com/PowerShell/feedbackdetail/view/1643846/).请投票! (3认同)
  • 尽管我自己回答了这个问题,但我接受@mathias-r-jessen的[答案](http://stackoverflow.com/a/31896279/450913),因为它可以更好地处理参数。 (2认同)