Eine bessere Möglichkeit, um zu überprüfen, ob in PowerShell ein Pfad vorhanden ist oder nicht

120

Ich mag einfach nicht die Syntax von:

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

und

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

Insbesondere gibt es zu viele Klammern und ist nicht sehr gut lesbar, wenn für eine so häufige Verwendung nach "nicht vorhanden" gesucht wird. Was ist ein besserer Weg, dies zu tun?

Update: Meine aktuelle Lösung besteht darin, Aliase für existund not-existwie hier erläutert zu verwenden .

Zugehöriges Problem im PowerShell-Repository: https://github.com/PowerShell/PowerShell/issues/1970

orad
quelle
2
Sie könnten verwendentry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris

Antworten:

130

Wenn Sie nur eine Alternative zur Cmdlet-Syntax speziell für Dateien wünschen, verwenden Sie die File.Exists().NET-Methode:

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

Wenn Sie andererseits einen für allgemeine Zwecke negierten Alias ​​wünschen Test-Path, sollten Sie dies folgendermaßen tun:

# 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

notexistsverhält sich jetzt genau so Test-Path, gibt aber immer das gegenteilige Ergebnis zurück:

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

Wie Sie bereits gezeigt haben, ist das Gegenteil ganz einfach, nur Alias existsfür Test-Path:

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True
Mathias R. Jessen
quelle
1
Wenn $pathes "speziell" ist, wie bei einem Powershell-Anbieter (denken Sie an HKLM: \ SOFTWARE \ ...), schlägt dies kläglich fehl.
Eris
4
@Eris Frage fragt speziell, ob eine Datei existiert oder nicht
Mathias R. Jessen
1
Auf jeden Fall ist es ordentlich, ein neues Cmdlet im laufenden Betrieb zu erstellen. Fast so unerreichbar wie ein Alias, aber immer noch sehr ordentlich :)
Eris
Nett! Ich denke, PS sollte native Unterstützung dafür hinzufügen.
Orad
4
@orad Ich bezweifle ernsthaft, dass Sie sie dazu bringen werden, das zu tun. "Zu viele Klammern" ist eine sehr subjektive Argumentation und verdient es nicht, von der Sprachgestaltung / -spezifikation abzuweichen. FWIW, ich stimme auch dem von @briantist vorgeschlagenen if / else-Konstrukt als bessere Alternative zu, wenn Sie Klammern wirklich so sehr hassen:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen
37

Die von Ihnen veröffentlichte Alias-Lösung ist clever, aber ich würde mich gegen die Verwendung in Skripten aus demselben Grund aussprechen, aus dem ich keine Aliase in Skripten verwenden möchte. Dies kann die Lesbarkeit beeinträchtigen.

Wenn dies etwas ist, das Sie Ihrem Profil hinzufügen möchten, damit Sie schnelle Befehle eingeben oder es als Shell verwenden können, dann könnte ich sehen, dass dies sinnvoll ist.

Sie könnten stattdessen Rohrleitungen in Betracht ziehen:

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

Alternativ können Sie für den negativen Ansatz, falls dies für Ihren Code angemessen ist, eine positive Prüfung durchführen und dann elsefür den negativen verwenden:

if (Test-Path $path) {
    throw "File already exists."
} else {
   # The thing you really wanted to do.
}
Briantist
quelle
1
Ich mag die Rohrleitungen hier, aber Ihre vorgeschlagenen Überprüfungen auf Negative sind ohne Klammern falsch, oder es wird immer ausgewertet False. Du musst es so machen if (-not ($path | Test-Path)) { ... }.
Orad
1
@orad du bist richtig! Eigentlich ist das in diesem Fall ein Nachteil der Rohrleitungen. Ich wurde in ein falsches Sicherheitsgefühl eingelullt, weil es keine Ausnahme auslöste, obwohl es tatsächlich fehlschlug. Wenn Sie es auf die ursprüngliche Weise nennen, wird eine Ausnahme ausgelöst, die es einfacher macht, das Problem zu erkennen.
Briantist
10

Fügen Sie die folgenden Aliase hinzu. Ich denke, diese sollten standardmäßig in PowerShell verfügbar gemacht werden:

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

Damit ändern sich die bedingten Anweisungen in:

if (exist $path) { ... }

und

if (not-exist $path)) { ... }
if (!exist $path)) { ... }
orad
quelle
4
Wenn Sie möchten, dass das PowerShell-Team einen "vorhandenen" Alias ​​hinzufügt, sollten Sie eine Funktionsanforderung über Microsoft Connect
Mathias R. Jessen
1
Obwohl ich es selbst beantwortet habe, akzeptiere ich die Antwort von @ mathias-r-jessen, weil sie Parameter besser handhabt.
Orad
2

Eine andere Option ist die Verwendung, IO.FileInfodie Ihnen so viele Dateiinformationen liefert, dass das Leben nur mit diesem Typ einfacher wird:

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

Weitere Details in meinem Blog.

VertigoRay
quelle
1

Verwenden Sie diesen Pfad, um zu überprüfen, ob ein Pfad zu einem Verzeichnis vorhanden ist:

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

Um zu überprüfen, ob ein Pfad zu einer Datei vorhanden ist, verwenden Sie die folgenden Vorschläge von @Mathias :

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

Dies ist meine Powershell-Neuling-Methode, um dies zu tun

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