Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 214 Vote(s) - 3.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
A better way to check if a path exists or not in PowerShell

#1
Is there a more concise and less error-prone way in PowerShell to check if a path DOES NOT exist?

This is objectively too verbose for such a common use case:

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

It needs too many parenthesis and is not very readable when checking for "not exist". It's also error-prone because a statement like:

if (-not $non_existent_path | Test-Path) { $true } else { $false }

will actually return `False`, when the user may expect `True`.

What is a better way to do this?

**Update 1:** My current solution is to use aliases for `exist` and `not-exist` as explained [here](

[To see links please register here]

).

**Update 2:** A proposed syntax that will also fix this is to allow the following grammar:

if !(expr) { statements* }
if -not (expr) { statements* }

Here's the related issue in PowerShell repository (please vote up 👍):

[To see links please register here]

Reply

#2
The alias solution you posted is clever, but I would argue against its use in scripts, for the same reason I don't like using any aliases in scripts; it tends to harm readability.

If this is something you want to add to your profile so you can type out quick commands or use it as a shell, then I could see that making sense.

You might consider piping instead:

<!-- language-all: lang-psh -->

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

Alternatively, for the negative approach, if appropriate for your code, you can make it a positive check then use `else` for the negative:

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

#3
If you just want an alternative to the cmdlet syntax, specifically for files, use the `File.Exists()` .NET method:

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

---

If, on the other hand, you want a general purpose negated alias for `Test-Path`, here is how you should do it:

# 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

`notexists` will now behave *exactly* like `Test-Path`, but always return the opposite result:

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

As you've already shown yourself, the opposite is quite easy, just alias `exists` to `Test-Path`:

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

#4
Another option is to use `IO.FileInfo` which gives you so much file info it make life easier just using this 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

[More details on my blog.](

[To see links please register here]

)
Reply

#5
To check if a Path exists to a directory, use this one:

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

To check if a Path to a file exists use what [@Mathias][1] suggested:

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


[1]:

[To see links please register here]

Reply

#6
Add the following aliases. I think these should be made available in PowerShell by default:

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

With that, the conditional statements will change to:

if (exist $path) { ... }

and

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

#7
```
if (Test-Path C:\DockerVol\SelfCertSSL) {
write-host "Folder already exists."
} else {
New-Item -Path "C:\DockerVol\" -Name "SelfCertSSL" -ItemType "directory"
}
```
Reply

#8
After looking at @Mathias R. Jessen's excellent answer, it occurred to me that you don't need to create two new functions. Instead, you can create a wrapper around the native `Test-Path` function with the same name that adds a `-Not` switch:

```
$TestPathCmd = Get-Command Test-Path
$TestPathCmdMetaData = New-Object System.Management.Automation.CommandMetadata $TestPathCmd
$Binding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($TestPathCmdMetaData)
$Params = [System.Management.Automation.ProxyCommand]::GetParamBlock($TestPathCmdMetaData)

$Params += ', [Switch]${Not}'
$WrappedCommand = {
$PSBoundParameters.Remove('Not') | Out-Null
[Bool]($Not.ToBool() -bxor (Microsoft.PowerShell.Management\Test-Path @PSBoundParameters))
}

${Function:Test-Path} = '{0} Param({1}) {2}' -f $Binding,$Params,$WrappedCommand
```

E.g.:

```
Test-Path -Path 'C:\Temp' # True
Test-Path -Path 'C:\Temp' -Not # False
Test-Path -Path 'C:\Txmp' # False
Test-Path -Path 'C:\Txmp' -Not # True
```

This has a couple of advantages:

1. Familiar syntax: when you're not using the custom switch, syntax is identical to the native command, and when you are it's pretty intuitive what's happening, which means less cognitive burden for the user, and more compatibility when sharing.
2. Because the wrapper is calling the native function under the hood, it will work anywhere the native function does, e.g.:
```
Test-Path -Path 'HKLM:\SOFTWARE' # True
Test-Path -Path 'HKLM:\SOFTWARE' -Not # False
Test-Path -Path 'HKLM:\SXFTWARE' # False
Test-Path -Path 'HKLM:\SXFTWARE' -Not # True
```
Reply

#9
This is my PowerShell newbie way of doing this

if (Test-Path ".\Desktop\checkfile.txt") {
Write-Host "Yay"
}
else {
Write-Host "Damn it"
}
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through