如何在PowerShell中检查文件是否在给定目录下?

时间:2022-06-01 20:57:43

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 .Parents, 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。