I want to check if a file path is in a given directory (or one of its subdirectories), from PowerShell.
我想从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 .Parent
s, but I was hoping for something simpler.
我可以拿$ file.Directory并迭代所有.Parents,但我希望更简单的东西。
EDIT: the file may not exist; I'm just looking at the path.
编辑:文件可能不存在;我只是在看路径。
5 个解决方案
#1
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.
这隐式使用-filter参数(按位置)指定foo.txt作为过滤器。您还可以指定* .txt或foo?.txt。 StartsWith的问题在于,当您处理不区分大小写的比较时,仍然存在/和\是PowerShell中的有效路径分隔符的问题。
Assuming the file may not exist and both $file and $directory are absolute paths, you can do this the "PowerShell" way:
假设文件可能不存在且$ file和$ directory都是绝对路径,您可以使用“PowerShell”方式执行此操作:
(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.:
但这并不是很好,因为你仍然需要规范路径/ - > \但至少PowerShell字符串比较不区分大小写。另一个选择是使用IO.Path规范化路径,例如:
[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".
这个问题的一个问题是,GetFullPath还会根据进程的当前目录使相对路径成为绝对路径,这种路径的次数与PowerShell的当前目录不同。所以只要确保$ directory是一个绝对路径,即使你必须像“$ pwd \ $ directory”那样指定它。
#2
Something like this?
像这样的东西?
Get-ChildItem -Recurse $directory | Where-Object { $_.PSIsContainer -and `
$_.FullName -match "^$($file.Parent)" } | Select-Object -First 1
#3
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).
由于路径可能不存在,因此使用string.StartsWith可以进行此类测试(尽管OrdinalIgnoreCase可以更好地表示文件系统如何比较路径)。
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:
唯一需要注意的是路径需要采用规范形式。否则,C:\ x \ .. \ a \ b.txt和C:/a/b.txt之类的路径将失败“这是在C:\ a \目录下”测试。在执行测试之前,可以使用静态Path.GetFullPath方法获取路径的全名:
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.
另请注意,这不包括逻辑包含(例如,如果你有\\ some \ network \ path \映射到Z:\ path \,测试\\ some \ network \ path \ b.txt是否在Z:\下会失败,即使可以通过Z:\ path \ b.txt访问该文件。如果您需要支持此行为,这些问题可能会有所帮助。
#4
If you convert your input strings to DirectoryInfo and FileInfo, you won't have any problem with the string comparison.
如果将输入字符串转换为DirectoryInfo和FileInfo,则字符串比较不会有任何问题。
function Test-FileInSubPath([System.IO.DirectoryInfo]$Dir,[System.IO.FileInfo]$File)
{
$File.FullName.StartsWith($Dir.FullName)
}
#5
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")
(在桌面上或桌面上的文件夹中创建新文件,并将其命名为“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.
当然迭代仍在发生,但PS正在为你做这件事。如果查找系统/隐藏文件,您也可能需要-force。
#1
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.
这隐式使用-filter参数(按位置)指定foo.txt作为过滤器。您还可以指定* .txt或foo?.txt。 StartsWith的问题在于,当您处理不区分大小写的比较时,仍然存在/和\是PowerShell中的有效路径分隔符的问题。
Assuming the file may not exist and both $file and $directory are absolute paths, you can do this the "PowerShell" way:
假设文件可能不存在且$ file和$ directory都是绝对路径,您可以使用“PowerShell”方式执行此操作:
(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.:
但这并不是很好,因为你仍然需要规范路径/ - > \但至少PowerShell字符串比较不区分大小写。另一个选择是使用IO.Path规范化路径,例如:
[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".
这个问题的一个问题是,GetFullPath还会根据进程的当前目录使相对路径成为绝对路径,这种路径的次数与PowerShell的当前目录不同。所以只要确保$ directory是一个绝对路径,即使你必须像“$ pwd \ $ directory”那样指定它。
#2
Something like this?
像这样的东西?
Get-ChildItem -Recurse $directory | Where-Object { $_.PSIsContainer -and `
$_.FullName -match "^$($file.Parent)" } | Select-Object -First 1
#3
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).
由于路径可能不存在,因此使用string.StartsWith可以进行此类测试(尽管OrdinalIgnoreCase可以更好地表示文件系统如何比较路径)。
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:
唯一需要注意的是路径需要采用规范形式。否则,C:\ x \ .. \ a \ b.txt和C:/a/b.txt之类的路径将失败“这是在C:\ a \目录下”测试。在执行测试之前,可以使用静态Path.GetFullPath方法获取路径的全名:
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.
另请注意,这不包括逻辑包含(例如,如果你有\\ some \ network \ path \映射到Z:\ path \,测试\\ some \ network \ path \ b.txt是否在Z:\下会失败,即使可以通过Z:\ path \ b.txt访问该文件。如果您需要支持此行为,这些问题可能会有所帮助。
#4
If you convert your input strings to DirectoryInfo and FileInfo, you won't have any problem with the string comparison.
如果将输入字符串转换为DirectoryInfo和FileInfo,则字符串比较不会有任何问题。
function Test-FileInSubPath([System.IO.DirectoryInfo]$Dir,[System.IO.FileInfo]$File)
{
$File.FullName.StartsWith($Dir.FullName)
}
#5
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")
(在桌面上或桌面上的文件夹中创建新文件,并将其命名为“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.
当然迭代仍在发生,但PS正在为你做这件事。如果查找系统/隐藏文件,您也可能需要-force。