How do I check if a file is under a given directory, in PowerShell? How do I check if a file is under a given directory, in PowerShell? powershell powershell

How do I check if a file is under a given directory, in PowerShell?


How about something as simple as:

PS> gci . -r foo.txt

This implicitly uses the -filter parameter (by position) specifying foo.txt as the filter. You could also specify *.txt or foo?.txt. The problem with StartsWith is that while you handle the case-insensitive compare there is still the issue that both / and \ are valid path separators in PowerShell.

Assuming the file may not exist and both $file and $directory are absolute paths, you can do this the "PowerShell" way:

(Split-Path $file -Parent) -replace '/','\' -eq (Get-Item $directory).FullName

But that isn't great since you still have to canonical the path / -> \ but at least the PowerShell string compare is case-insensitive. Another option is to use IO.Path to canonicalize the path e.g.:

[io.path]::GetDirectoryName($file) -eq [io.path]::GetFullPath($directory)

One issue with this is that GetFullPath will also make a relative path an absolute path based on the process's current dir which more times than not, is not the same as PowerShell's current dir. So just make sure $directory is an absolute path even if you have to specify it like "$pwd\$directory".


Since the path might not exist, using string.StartsWith is fine for doing this type of test (though OrdinalIgnoreCase is a better representation of how the file system compares paths).

The only caveat is that the paths need to be in a canonical form. Otherwise, paths like C:\x\..\a\b.txt and C:/a/b.txt would fail the "is this under the C:\a\ directory" test. You can use the static Path.GetFullPath method to get the full names of the paths before you do the test:

function Test-SubPath( [string]$directory, [string]$subpath ) {  $dPath = [IO.Path]::GetFullPath( $directory )  $sPath = [IO.Path]::GetFullPath( $subpath )  return $sPath.StartsWith( $dPath, [StringComparison]::OrdinalIgnoreCase )}

Also note that this does not cover logical containment (e.g. if you have \\some\network\path\ mapped to Z:\path\, testing whether \\some\network\path\b.txt is under Z:\ will fail, even though the file can be accessed through Z:\path\b.txt). If you need to support this behavior, these questions might help.


Something like this?

Get-ChildItem -Recurse $directory | Where-Object { $_.PSIsContainer -and `    $_.FullName -match "^$($file.Parent)" } | Select-Object -First 1