Cara yang lebih baik untuk memeriksa apakah ada jalur atau tidak di PowerShell

122

Saya hanya tidak suka sintaks:

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

dan

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

terutama ada terlalu banyak tanda kurung dan tidak terlalu bisa dibaca saat memeriksa "tidak ada" untuk penggunaan yang umum. Apa cara yang lebih baik untuk melakukan ini?

Pembaruan: Solusi saya saat ini adalah menggunakan alias untuk existdan not-existseperti yang dijelaskan di sini .

Masalah terkait di penyimpanan PowerShell: https://github.com/PowerShell/PowerShell/issues/1970

orad
sumber
2
Anda bisa menggunakantry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris

Jawaban:

130

Jika Anda hanya ingin alternatif sintaks cmdlet, khusus untuk file, gunakan File.Exists()metode .NET:

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

Jika, di sisi lain, Anda menginginkan alias negasi untuk tujuan umum Test-Path, berikut adalah cara melakukannya:

# 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

notexistssekarang akan berperilaku persis seperti Test-Path, tetapi selalu mengembalikan hasil yang berlawanan:

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

Seperti yang sudah Anda tunjukkan pada diri Anda sendiri, sebaliknya cukup mudah, alias hanya existsuntuk Test-Path:

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True
Mathias R. Jessen
sumber
1
Jika $path"spesial", seperti pada Powershell Provider (pikirkan HKLM: \ SOFTWARE \ ...) maka ini akan gagal total.
Eris
4
Pertanyaan @Eris secara khusus meminta untuk memeriksa apakah ada file atau tidak
Mathias R. Jessen
1
Pastinya, dan membuat cmdlet baru dengan cepat itu rapi. Hampir tidak bisa dipertahankan sebagai alias, tapi masih sangat rapi :)
Eris
Bagus! Saya pikir PS harus menambahkan dukungan asli untuk ini.
orad
4
@ orad Saya benar-benar ragu Anda akan membuat mereka melakukan itu. "Terlalu banyak tanda kurung" adalah alasan yang sangat subjektif dan tidak pantas menyimpang dari desain / spesifikasi bahasa. FWIW, saya juga setuju dengan konstruk if / else yang diusulkan oleh @briantist sebagai alternatif yang lebih baik jika Anda sangat membenci tanda kurung:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen
38

Solusi alias yang Anda posting pintar, tetapi saya akan membantah penggunaannya dalam skrip, karena alasan yang sama saya tidak suka menggunakan alias apa pun dalam skrip; itu cenderung merusak keterbacaan.

Jika ini adalah sesuatu yang ingin Anda tambahkan ke profil Anda sehingga Anda dapat mengetikkan perintah cepat atau menggunakannya sebagai shell, maka saya bisa melihat itu masuk akal.

Anda dapat mempertimbangkan pemipaan sebagai gantinya:

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

Atau, untuk pendekatan negatif, jika sesuai untuk kode Anda, Anda dapat membuatnya sebagai tanda centang positif lalu gunakan elseuntuk yang negatif:

if (Test-Path $path) {
    throw "File already exists."
} else {
   # The thing you really wanted to do.
}
briantist
sumber
1
Saya suka penyisipan di sini, tetapi pemeriksaan yang Anda usulkan untuk negatif salah tanpa tanda kurung, atau akan selalu dievaluasi ke False. Anda perlu melakukannya seperti if (-not ($path | Test-Path)) { ... }.
orad
1
@ orad Anda benar! Sebenarnya itu negatif dari perpipaan dalam kasus itu. Saya terbuai ke dalam rasa aman yang palsu karena tidak membuat pengecualian, padahal ternyata itu gagal. Menyebutnya dengan cara asli akan membuat pengecualian, membuatnya lebih mudah untuk menangkap masalahnya.
briantist
10

Tambahkan alias berikut. Saya pikir ini harus tersedia di PowerShell secara default:

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

Dengan itu, pernyataan kondisional akan berubah menjadi:

if (exist $path) { ... }

dan

if (not-exist $path)) { ... }
if (!exist $path)) { ... }
orad
sumber
4
Jika Anda ingin tim PowerShell menambahkan alias "ada", Anda harus mengirimkan permintaan fitur melalui Microsoft Connect
Mathias R. Jessen
1
Meskipun saya menjawab sendiri, saya menerima @ mathias-r-Jessen ini jawabannya karena menangani parameter yang lebih baik.
orad
2

Opsi lain adalah menggunakan IO.FileInfoyang memberi Anda begitu banyak info file sehingga membuat hidup lebih mudah hanya dengan menggunakan jenis ini:

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

Lebih detail di blog saya.

VertigoRay
sumber
1

Untuk memeriksa apakah Path ada ke direktori, gunakan yang ini:

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

Untuk memeriksa apakah Path ke file ada, gunakan apa yang disarankan @Mathias :

[System.IO.File]::Exists($pathToAFile)
shaheen g
sumber
0

Ini adalah cara pemula saya yang bertenaga untuk melakukan ini

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
David Bohbot
sumber