Un meilleur moyen de vérifier si un chemin existe ou non dans PowerShell

123

Je n'aime tout simplement pas la syntaxe de:

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

et

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

surtout il y a trop de parenthèses et pas très lisible lors de la vérification de "pas exister" pour une utilisation aussi courante. Quelle est la meilleure façon de procéder?

Mise à jour: Ma solution actuelle consiste à utiliser des alias pour existet not-existcomme expliqué ici .

Problème associé dans le référentiel PowerShell: https://github.com/PowerShell/PowerShell/issues/1970

orad
la source
2
Vous pouvez utilisertry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris
1
Problème
orad

Réponses:

130

Si vous souhaitez simplement une alternative à la syntaxe de l'applet de commande, en particulier pour les fichiers, utilisez la File.Exists()méthode .NET:

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

Si, d'un autre côté, vous voulez un alias annulé à usage général Test-Path, voici comment procéder:

# 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

notexistsse comportera désormais exactement comme Test-Path, mais retournera toujours le résultat opposé:

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

Comme vous l'avez déjà montré, l'inverse est assez simple, il suffit d'alias existspour Test-Path:

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True
Mathias R. Jessen
la source
1
Si $pathc'est "spécial", comme sur un fournisseur Powershell (pensez à HKLM: \ SOFTWARE \ ...) alors cela échouera lamentablement.
Eris
4
@Eris question demande spécifiquement de vérifier si un fichier existe ou non
Mathias R. Jessen
1
Certainement, et la création d'une nouvelle applet de commande à la volée est bien. Presque aussi impossible à maintenir qu'un alias, mais toujours très soigné :)
Eris
Agréable! Je pense que PS devrait ajouter un support natif pour cela.
orad
4
@orad Je doute sérieusement que vous leur fassiez cela. «Trop de parenthèses» est un raisonnement très subjectif et ne mérite pas vraiment de s'écarter de la conception / spécification du langage. FWIW, je suis également d'accord avec la construction if / else proposée par @briantist comme une meilleure alternative si vous détestez vraiment autant les parenthèses:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen
38

La solution d'alias que vous avez publiée est intelligente, mais je suis contre son utilisation dans les scripts, pour la même raison que je n'aime pas utiliser d'alias dans les scripts; cela a tendance à nuire à la lisibilité.

Si c'est quelque chose que vous souhaitez ajouter à votre profil afin que vous puissiez taper des commandes rapides ou l'utiliser comme shell, alors je pourrais voir que cela a du sens.

Vous pouvez plutôt envisager la tuyauterie:

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

Alternativement, pour l'approche négative, si cela est approprié pour votre code, vous pouvez en faire un contrôle positif puis utiliser elsepour le négatif:

if (Test-Path $path) {
    throw "File already exists."
} else {
   # The thing you really wanted to do.
}
briantiste
la source
1
J'aime la tuyauterie ici, mais vos vérifications proposées pour les négatifs sont incorrectes sans parenthèses, ou elles seront toujours évaluées False. Vous devez le faire comme if (-not ($path | Test-Path)) { ... }.
orad
1
@orad vous avez raison! En fait, c'est un inconvénient de la tuyauterie dans ce cas. J'étais bercé par un faux sentiment de sécurité en ne lançant pas d'exception, alors qu'il échouait. L'appeler de la manière originale lève une exception, ce qui facilite la détection du problème.
briantist
10

Ajoutez les alias suivants. Je pense que ceux-ci devraient être disponibles dans PowerShell par défaut:

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

Avec cela, les instructions conditionnelles deviendront:

if (exist $path) { ... }

et

if (not-exist $path)) { ... }
if (!exist $path)) { ... }
orad
la source
4
Si vous souhaitez que l'équipe PowerShell ajoute un alias "exist", vous devez soumettre une demande de fonctionnalité via Microsoft Connect
Mathias R. Jessen
1
Même si j'y ai répondu moi-même, j'accepte la réponse de @ mathias-r-jessen car elle gère mieux les paramètres.
orad
2

Une autre option consiste à utiliser IO.FileInfoce qui vous donne tellement d'informations sur les fichiers qu'il vous facilite la vie en utilisant simplement ce type:

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

Plus de détails sur mon blog.

VertigoRay
la source
1

Pour vérifier s'il existe un chemin vers un répertoire, utilisez celui-ci:

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

Pour vérifier si un chemin vers un fichier existe, utilisez ce que @Mathias a suggéré:

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

C'est ma façon novatrice de PowerShell de faire ça

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