Một cách tốt hơn để kiểm tra xem một đường dẫn có tồn tại hay không trong PowerShell


122

Tôi chỉ không thích cú pháp của:

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

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

đặc biệt là có quá nhiều dấu ngoặc đơn và không dễ đọc lắm khi kiểm tra "không tồn tại" cho mục đích sử dụng phổ biến như vậy. Cách tốt hơn để làm điều này là gì?

Cập nhật: Giải pháp hiện tại của tôi là sử dụng bí danh cho existnot-existnhư được giải thích ở đây .

Sự cố liên quan trong kho lưu trữ PowerShell: https://github.com/PowerShell/PowerShell/issues/1970


2
Bạn có thể sử dụngtry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris

Câu trả lời:


130

Nếu bạn chỉ muốn thay thế cho cú pháp lệnh ghép ngắn, cụ thể cho các tệp, hãy sử dụng File.Exists()phương thức .NET:

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

Mặt khác, nếu bạn muốn có một bí danh bị phủ định với mục đích chung Test-Path, thì đây là cách bạn nên thực hiện:

# 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

notexistsbây giờ sẽ hoạt động giống hệt như vậy Test-Path, nhưng luôn trả về kết quả ngược lại:

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

Như bạn đã thể hiện chính mình, điều ngược lại khá dễ dàng, chỉ cần bí danh existsTest-Path:

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

1
Nếu $pathlà "đặc biệt", như trên Nhà cung cấp Powershell (hãy nghĩ HKLM: \ SOFTWARE \ ...) thì điều này sẽ thất bại thảm hại.
Eris

4
Câu hỏi @Eris yêu cầu cụ thể để kiểm tra xem tệp có tồn tại hay không
Mathias R. Jessen

1
Chắc chắn, và tạo một lệnh ghép ngắn mới một cách nhanh chóng. Gần như không thể nhầm lẫn được như một bí danh, nhưng vẫn thực sự gọn gàng :)
Eris

Đẹp! Tôi nghĩ PS nên thêm hỗ trợ gốc cho việc này.
orad

4
@orad Tôi thực sự nghi ngờ bạn sẽ khiến họ làm điều đó. "Quá nhiều dấu ngoặc đơn" là một suy luận rất chủ quan và không thực sự xứng đáng khi đi chệch khỏi thiết kế / đặc tả ngôn ngữ. FWIW, tôi cũng đồng ý với if / else xây dựng bởi @briantist đề xuất như là một lựa chọn tốt hơn nếu bạn thực sự ghét ngoặc rằng có rất nhiều:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen

38

Giải pháp bí danh bạn đã đăng rất thông minh, nhưng tôi sẽ phản đối việc sử dụng nó trong script, vì lý do tương tự mà tôi không thích sử dụng bất kỳ bí danh nào trong script; nó có xu hướng gây hại cho khả năng đọc.

Nếu đây là thứ bạn muốn thêm vào hồ sơ của mình để bạn có thể gõ các lệnh nhanh hoặc sử dụng nó như một trình bao, thì tôi có thể thấy điều đó có ý nghĩa.

Thay vào đó, bạn có thể xem xét đường ống:

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

Ngoài ra, đối với cách tiếp cận phủ định, nếu phù hợp với mã của bạn, bạn có thể kiểm tra tích cực rồi sử dụng elsecho tiêu cực:

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

1
Tôi thích đường ống ở đây, nhưng kiểm tra phủ định được đề xuất của bạn không chính xác mà không có dấu ngoặc đơn, hoặc nó sẽ luôn đánh giá False. Bạn cần phải làm điều đó như thế nào if (-not ($path | Test-Path)) { ... }.
orad

1
@orad bạn nói đúng! Trên thực tế, đó là một tiêu cực của đường ống trong trường hợp đó. Tôi đã bị ru ngủ vào một cảm giác an toàn sai lầm bởi nó không ném ra một ngoại lệ, trong khi thực tế là nó đang thất bại. Gọi nó theo cách ban đầu ném ra một ngoại lệ, giúp bạn dễ dàng nắm bắt vấn đề hơn.
briantist

10

Thêm các bí danh sau. Tôi nghĩ những điều này sẽ được cung cấp trong PowerShell theo mặc định:

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

Với điều đó, các câu lệnh điều kiện sẽ thay đổi thành:

if (exist $path) { ... }

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

4
Nếu bạn muốn nhóm PowerShell thêm bí danh "tồn tại", bạn nên gửi yêu cầu tính năng thông qua Microsoft Connect
Mathias R. Jessen

1
Mặc dù tôi đã tự trả lời, tôi chấp nhận câu trả lời của @ mathias-r-jessen vì nó xử lý các thông số tốt hơn.
orad

2

Một tùy chọn khác là sử dụng tùy chọn này IO.FileInfocung cấp cho bạn rất nhiều thông tin tệp, giúp cuộc sống dễ dàng hơn chỉ cần sử dụng loại này:

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

Thêm chi tiết trên blog của tôi.


1

Để kiểm tra xem Đường dẫn có tồn tại trong một thư mục hay không, hãy sử dụng đường dẫn này:

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

Để kiểm tra xem Đường dẫn đến tệp có tồn tại hay không, hãy sử dụng những gì @Mathias đề xuất:

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

0

Đây là cách mới của tôi để làm điều này

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.