Un modo migliore per verificare se un percorso esiste o meno in PowerShell


122

Semplicemente non mi piace la sintassi di:

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

e

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

in particolare ci sono troppe parentesi e poco leggibili quando si controlla "non esiste" per un uso così comune. Qual è un modo migliore per farlo?

Aggiornamento: la mia soluzione attuale è usare gli alias per existe not-existcome spiegato qui .

Problema correlato nel repository di PowerShell: https://github.com/PowerShell/PowerShell/issues/1970


2
Potresti usaretry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris

Risposte:


130

Se desideri solo un'alternativa alla sintassi del cmdlet, in particolare per i file, utilizza il File.Exists()metodo .NET:

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

Se, d'altra parte, vuoi un alias negato per scopi generali Test-Path, ecco come dovresti farlo:

# 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

notexistsora si comporterà esattamente come Test-Path, ma restituirà sempre il risultato opposto:

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

Come hai già dimostrato, l'opposto è abbastanza facile, solo alias existsper Test-Path:

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

1
Se $pathè "speciale", come su un provider Powershell (pensa HKLM: \ SOFTWARE \ ...), allora fallirà miseramente.
Eris

4
La domanda di @Eris chiede specificamente di verificare se un file esiste o meno
Mathias R. Jessen

1
Sicuramente, e la creazione di un nuovo cmdlet al volo è semplice. Quasi impossibile da mantenere come un alias, ma comunque davvero pulito :)
Eris

Bello! Penso che PS dovrebbe aggiungere il supporto nativo per questo.
orad

4
@orad Dubito seriamente che li convincerai a farlo. "Troppe parentesi" è un ragionamento molto soggettivo e non merita davvero di deviare dalla progettazione / specifica del linguaggio. FWIW, sono anche d'accordo con il costrutto if / else proposto da @briantist come alternativa migliore se davvero odi così tanto le parentesi:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen

38

La soluzione di alias che hai postato è intelligente, ma direi contro il suo uso negli script, per lo stesso motivo per cui non mi piace usare alcun alias negli script; tende a danneggiare la leggibilità.

Se questo è qualcosa che vuoi aggiungere al tuo profilo in modo da poter digitare comandi rapidi o usarlo come shell, allora potrei vedere che ha senso.

Potresti invece considerare il piping:

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

In alternativa, per l'approccio negativo, se appropriato per il tuo codice, puoi renderlo un controllo positivo quindi utilizzare elseper il negativo:

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

1
Mi piace il piping qui, ma i tuoi controlli proposti per i negativi non sono corretti senza parentesi, o valuteranno sempre False. Devi farlo come if (-not ($path | Test-Path)) { ... }.
orad

1
@orad hai ragione! In realtà questo è un aspetto negativo delle tubazioni in quel caso. Sono stato cullato in un falso senso di sicurezza dal fatto che non ha lanciato un'eccezione, quando in realtà stava fallendo. Chiamarlo nel modo originale genera un'eccezione, rendendo più facile individuare il problema.
Briantist

10

Aggiungi i seguenti alias. Penso che questi dovrebbero essere resi disponibili in PowerShell per impostazione predefinita:

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

Con ciò, le istruzioni condizionali cambieranno in:

if (exist $path) { ... }

e

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

4
Se si desidera che il team di PowerShell aggiunga un alias "esiste", è necessario inviare una richiesta di funzionalità tramite Microsoft Connect
Mathias R. Jessen

1
Anche se ho risposto io stesso, accetto la risposta di @ mathias-r-jessen perché gestisce meglio i parametri.
orad

2

Un'altra opzione è quella di utilizzare IO.FileInfoche ti dà così tante informazioni sui file da rendere la vita più facile usando solo questo tipo:

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

Maggiori dettagli sul mio blog.


1

Per verificare se esiste un percorso per una directory, usa questo:

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

Per verificare se esiste un percorso per un file, utilizzare quanto suggerito da @Mathias :

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

0

Questo è il mio modo per principianti di PowerShell per farlo

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.