7

I want to check if a file path is in a given directory (or one of its subdirectories), from PowerShell.

Right now I'm doing:

$file.StartsWith(  $directory, [StringComparison]::InvariantCultureIgnoreCase )

but I'm sure there are better ways.

I could do take $file.Directory and iterate over all .Parents, but I was hoping for something simpler.

EDIT: the file may not exist; I'm just looking at the path.

5 Answers 5

6

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".

Sign up to request clarification or add additional context in comments.

1 Comment

+1. Being able to specify -path and -fil separately is very convenient. Learn it, love it.
2

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.

Comments

1

If you convert your input strings to DirectoryInfo and FileInfo, you won't have any problem with the string comparison.

function Test-FileInSubPath([System.IO.DirectoryInfo]$Dir,[System.IO.FileInfo]$File)
{
    $File.FullName.StartsWith($Dir.FullName)
}

Comments

0

Something like this?

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

Comments

0

Something real quick:

14:47:28 PS>pwd

C:\Documents and Settings\me\Desktop

14:47:30 PS>$path = pwd

14:48:03 PS>$path

C:\Documents and Settings\me\Desktop

14:48:16 PS>$files = Get-ChildItem $path -recurse | 
                     Where {$_.Name -match "thisfiledoesnt.exist"}

14:50:55 PS>if ($files) {write-host "the file exists in this path somewhere"
            } else {write-host "no it doesn't"}
no it doesn't

(create new file on desktop or in a folder on the desktop and name it "thisfileexists.txt")

14:51:03 PS>$files = Get-ChildItem $path -recurse | 
                     Where {$_.Name -match "thisfileexists.txt"}

14:52:07 PS>if($files) {write-host "the file exists in this path somewhere"
            } else {write-host "no it doesn't"}
the file exists in this path somewhere

Of course iterating is still happening, but PS is doing it for you. You also might need -force if looking for system/hidden files.

3 Comments

I'm not interested in whether the file exists, but whether its path is under a directory. Also, your formatting is unclear.
Yeah, the code formatting here doesn't work well for PS. This is not a full PS1 file, just stuff typed on the PS cmd line. Your question asked "How do I check if a file is under a given directory..." It looks like the other answer is also checking for a file, as you asked. I see your edit, but how can a file have a path if it doesn't even exist?
I think I get it now, you have this file path stored somewhere, and you want to see if the path part exists beneath some other specified path?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.