PowerShell 如何处理“."在路径中? [英] How does PowerShell treat "." in paths?
问题描述
在打开 PowerShell 终端时考虑以下命令序列:
Consider the following sequence of commands upon opening a PowerShell terminal:
PS C:\Users\username> cd source
PS C:\Users\username\source> $dir = ".\temp"
PS C:\Users\username\source> [System.IO.Path]::GetFullPath($dir)
C:\Users\username\temp
现在这个:
PS C:\Users\username> cd source
PS C:\Users\username\source> powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\username\source> $dir = ".\temp"
PS C:\Users\username\source> [System.IO.Path]::GetFullPath($dir)
C:\Users\username\source\temp
为什么 PowerShell 解释."相对于启动 PowerShell 的目录,而不是当前目录?我怎样才能让它解释."相对于当前目录?
Why does PowerShell interpret "." relative to the directory in which PowerShell was started, rather than the current directory? How can I get it to interpret "." relative to the current directory?
推荐答案
正如 js2010 的有用答案 所述,它是使用引入问题的.NET方法:
.NET 的单个进程范围当前目录通常和设计[1] 不同于 PowerShell 的运行空间特定 一个.
As js2010's helpful answer states, it is the use of a .NET method that introduces the problem:
.NET's single, process-wide current directory typically and by design[1] differs from PowerShell's runspace-specific one.
这具有以下含义:
因为 PowerShell 本身确实可靠地将
.
解释为当前的位置(这是 PowerShell 的概括当前目录的概念也可以引用其他类型的位置,在其他 PowerShell 驱动器提供程序公开的驱动器上,例如 注册表 提供程序),您可以使用PowerShell 命令(如果可用)避免该问题.
Since PowerShell itself does reliably interpret
.
as the current location (which is PowerShell's generalization of the concept of a current directory that can refer to other types of locations as well, on drives exposed by other PowerShell drive providers, such as the registry provider), you can avoid the problem by using PowerShell commands, if available.
当您调用 .NET 方法时,请确保预先将任何相对路径解析为绝对路径,或者在支持的情况下,另外提供当前的 PowerShell 文件系统location 作为 reference 目录 - 这避免了当前目录不匹配的问题.
When you do call .NET methods, be sure to resolve any relative paths to absolute ones beforehand or, where supported, additionally supply the current PowerShell filesystem location as a reference directory - this avoids the problem of the mismatched current directories.
- (另一个但次优的选择是每次传递相对路径时首先设置
[Environment]::CurrentDirectory = $PWD.ProviderPath
,但这是笨拙的,如果同一进程中可能存在多个 PowerShell 运行空间.)
- (Another, but suboptimal option is to first set
[Environment]::CurrentDirectory = $PWD.ProviderPath
every time a relative path is passed, but that is clumsy and shouldn't be used if there's a chance that multiple PowerShell runspaces exist in the same process.)
下一部分展示了如何将相对 PowerShell 路径安全地传递给 .NET 方法,而底部部分则解决了您问题中的具体问题:如何将任意给定的 PowerShell 路径解析为绝对的本机文件系统路径.
The next section shows how to safely pass relative PowerShell paths to .NET methods, whereas the bottom section solves the specific problem in your question: how to resolve an arbitrary, given PowerShell path to an absolute, native filesystem path.
如前所述,当前目录中的差异要求将绝对路径传递给 .NET 方法,该路径基于 PowerShell 的当前目录.
As stated, the discrepancy in current directories requires that an absolute path be passed to .NET methods, arrived at based on PowerShell's current directory.
示例假设将相对路径 someFile.txt
传递给 .NET 方法 [IO.File]::ReadAllText()
The examples assume relative path someFile.txt
to be passed to .NET method [IO.File]::ReadAllText()
注意使用了简单的字符串插值,用/
(可以与\
互换使用)来连接路径组件;如果当前目录恰好是 root 目录,您将得到 2 个路径分隔符,但这不会影响功能.但是,如果您仍然需要避免这种情况,请改用 Join-Path
cmdlet.
Note that simple string interpolation is used, with /
(which can be used interchangeably with \
) used to join the path components; if the current directory happens to be the root directory, you'll end up with 2 path separators, but that doesn't affect functionality. If you still need to avoid that, however, use the Join-Path
cmdlet instead.
- 最简单,但不完全可靠,通过
$PWD
(如果当前目录基于 PowerShell 特定驱动器New-PsDrive
或者如果当前位置不是 filesystem 位置):
- Simplest, but not fully robust, via
$PWD
(fails if the current directory is based on a PowerShell-specific drive created withNew-PsDrive
or if the current location is not a filesystem location):
[IO.File]::ReadAllText("$PWD/someFile.txt")
- 更强大:通过
$PWD.ProviderPath
(将基于 PowerShell 驱动器的路径解析为底层本机文件系统路径,但如果当前位置不是文件系统位置): - More robust: via
$PWD.ProviderPath
(resolves a PowerShell drive-based path to the underlying native filesystem path, but can still fail if the current location is not a filesystem location):
[IO.File]::ReadAllText("$($PWD.ProviderPath)/someFile.txt")
- 完全可靠:通过
(Get-Location -PSProvider FileSystem).ProviderPath
[IO.File]::ReadAllText("$((Get-Location -PSProvider FileSystem).ProviderPath)/someFile.txt")
注意:以上适用于存在和不存在的路径;如果已知路径存在 - 例如使用 [IO.File]::ReadAllText()
,而不是 [IO.File]::WriteAllText()
- 您也可以使用以下内容,但前提是您可以进一步假设当前位置是 filesystem 位置:
Note: The above works with both existent and nonexistent paths; if the path is known to exist - such as with [IO.File]::ReadAllText()
, as opposed to [IO.File]::WriteAllText()
- you can also use the following, but only if you can further assume that the current location is a filesystem location:
[IO.File]::ReadAllText((Convert-Path -LiteralPath someFile.txt))
Convert-Path
和 Resolve-Path
仅适用于 现有 路径(从 PowerShell Core 7.0.0-preview.3 开始)很不幸;在 GitHub 上提议.
That Convert-Path
and Resolve-Path
only work with existing paths (as of PowerShell Core 7.0.0-preview.3) is unfortunate; providing an opt-in for nonexistent path has been proposed on GitHub.
同样,如果 Convert-Path
和 Resolve-Path
支持 -PSProvider
参数以允许明确指定目标提供程序,那将会很有帮助,因为 Get-Location
已经支持 - 请参阅 GitHub 上的此建议一>.
Similarly, it would be helpful if Convert-Path
and Resolve-Path
supported a -PSProvider
parameter to allow specifying the target provider explicitly, as Get-Location
already supports - see this suggestion on GitHub.
如果路径存在,使用 Convert-Path
将任何 PowerShell 文件系统路径解析为绝对的、文件系统原生路径:>
$dir = "./temp"
Convert-Path -LiteralPath $dir
相关的 Resolve-Path
cmdlet 提供了类似的功能,但它不解析基于 PowerShell 特定驱动器(使用 New-PsDrive
) 到其底层本机文件系统路径.
The related Resolve-Path
cmdlet provides similar functionality, but it doesn't resolve paths based on PowerShell-specific drives (created with New-PsDrive
) to their underlying native filesystem paths.
如果路径不存在(还):
If the path doesn't exist (yet):
在基于 .NET Core 的 PowerShell Core 中,您可以使用新的 [IO.Path]::GetFullPath()
重载,接受指定相对路径的引用目录:
$dir = "./temp"
[IO.Path]::GetFullPath($dir, $PWD.ProviderPath)
注意当前位置的本机文件系统路径 $PWD.ProviderPath
如何作为参考目录传递.
Note how the current location's native filesystem path, $PWD.ProviderPath
, is passed as the reference directory.
警告:如果当前位置可能位于文件系统驱动器以外的驱动器上,请使用 (Get-Location -PSProviderFileSystem).ProviderPath
可靠地引用当前文件系统位置(目录).
Caveat: If there's a chance that the current location is on a drive other than a filesystem drive, use (Get-Location -PSProvider FileSystem).ProviderPath
to reliably refer to the current filesystem location (directory).
在Windows PowerShell中,您可以使用[IO.Path]::Combine()
,但是请注意,如果您不希望在结果路径中使用 ./
前缀,则必须手动删除它:
In Windows PowerShell, you can use [IO.Path]::Combine()
, but note that you'll have to remove the ./
prefix manually if you don't want it in the resulting path:
$dir = "./temp"
[IO.Path]::Combine($PWD.ProviderPath, $dir -replace '^.[\\/]')
<小时>
[1] 虽然一个给定的进程通常只有 一个 PowerShell 运行空间(会话),但是 多个 一个进程共存的可能性意味着它从概念上讲,他们所有人都无法将各自的工作目录与唯一一个进程范围的 .NET 工作目录同步.如需更深入的解释,请参阅此 GitHub 问题.
[1] While a given process typically has only one PowerShell runspace (session), the potential for multiple ones to coexist in a process means that it is conceptually impossible for all of them to sync their individual working directories with the one and only process-wide .NET working directory. For a more in-depth explanation, see this GitHub issue.
这篇关于PowerShell 如何处理“."在路径中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!