检查PowerShell中是否存在路径的更好方法[关闭]

nsc4cvqm  于 2023-03-30  发布在  Shell
关注(0)|答案(8)|浏览(133)

已关闭。此问题为opinion-based。当前不接受答案。
**想要改进此问题吗?**请更新此问题,以便editing this post可以用事实和引文来回答。

1年前关闭。
这篇文章被编辑并提交审查1年前,未能重新打开帖子:
原始关闭原因未解决
Improve this question
PowerShell中是否有更简洁、更少出错的方法来检查路径是否不存在?
对于这样一个常见的用例来说,这在客观上过于冗长:

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

它需要太多的括号,并且在检查“不存在”时不是很可读。它也容易出错,因为像这样的语句:

if (-not $non_existent_path | Test-Path) { $true } else { $false }

实际上将返回False,而用户可能期望True
什么是更好的方法来做到这一点?

**更新1:**我目前的解决方案是使用existnot-exist的别名,如here所述。
**更新2:**一个建议的语法也将解决这个问题,允许以下语法:

if !(expr) { statements* }
if -not (expr) { statements* }

以下是PowerShell存储库中的相关问题(请投票支持👍):https://github.com/PowerShell/PowerShell/issues/1970

3zwjbxry

3zwjbxry1#

如果您只想使用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

正如您已经展示的那样,相反的操作非常简单,只需将exists别名为Test-Path

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

zlhcx6iw2#

你发布的别名解决方案是聪明的,但我反对在脚本中使用它,因为同样的原因,我不喜欢在脚本中使用任何别名;它倾向于损害可读性。
如果你想把它添加到你的配置文件中,这样你就可以输入快速命令或者把它用作shell,那么我可以看到这是有意义的。
您可以考虑使用管道:

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.
}
nlejzf6q

nlejzf6q3#

添加以下别名。我认为默认情况下,这些别名应该在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) { ... }
au9on6nz

au9on6nz4#

这是我的PowerShell新手做这件事的方式

if (Test-Path ".\Desktop\checkfile.txt") {
    Write-Host "Yay"
} 
else {
    Write-Host "Damn it"
}
6yt4nkrj

6yt4nkrj5#

另一种选择是使用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

More details on my blog.

jvlzgdj9

jvlzgdj96#

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

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

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

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

qnzebej07#

在看了@Mathias R. Jessen的精彩回答后,我突然想到,你不需要创建两个新函数。相反,你可以创建一个 Package 器,围绕原生Test-Path函数使用相同的名称,添加一个-Not开关:

$TestPathCmd = Get-Command Test-Path
$TestPathCmdMetaData = New-Object System.Management.Automation.CommandMetadata $TestPathCmd
$Binding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($TestPathCmdMetaData)
$Params  = [System.Management.Automation.ProxyCommand]::GetParamBlock($TestPathCmdMetaData)

$Params += ', [Switch]${Not}'
$WrappedCommand = {
    $PSBoundParameters.Remove('Not') | Out-Null
    [Bool]($Not.ToBool() -bxor (Microsoft.PowerShell.Management\Test-Path @PSBoundParameters))
}

${Function:Test-Path} = '{0} Param({1}) {2}' -f $Binding,$Params,$WrappedCommand

例如:

Test-Path -Path 'C:\Temp'      # True
Test-Path -Path 'C:\Temp' -Not # False
Test-Path -Path 'C:\Txmp'      # False
Test-Path -Path 'C:\Txmp' -Not # True

这有几个优点:
1.熟悉的语法:当你不使用自定义开关时,语法与本机命令相同,当你使用时,它会非常直观地显示发生了什么,这意味着用户的认知负担更少,共享时的兼容性更好。
1.因为 Package 器在后台调用本机函数,所以它将在本机函数的任何地方工作,例如:

Test-Path -Path 'HKLM:\SOFTWARE'      # True
Test-Path -Path 'HKLM:\SOFTWARE' -Not # False
Test-Path -Path 'HKLM:\SXFTWARE'      # False
Test-Path -Path 'HKLM:\SXFTWARE' -Not # True
c90pui9n

c90pui9n8#

if (Test-Path C:\DockerVol\SelfCertSSL) {
    write-host "Folder already exists."
} else {
   New-Item -Path "C:\DockerVol\" -Name "SelfCertSSL" -ItemType "directory"
}

相关问题