PowerShell 如何处理“.”在路径中?
打开 PowerShell 终端时请考虑以下命令序列:
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 的目录,而不是当前目录?我怎样才能让它解释“。”相对于当前目录?
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
Now this:
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
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
正如 js2010 的有用答案所述,使用 .NET 方法引入问题:
.NET 的单个进程范围当前目录通常按照设计[1]不同于 PowerShell 的特定于运行空间< /em> 一个。
这具有以下含义:
由于 PowerShell 本身确实可靠地将
.
解释为当前位置< /em> (即PowerShell 对当前目录概念的概括,该目录也可以引用其他 PowerShell 驱动器提供程序(例如注册表提供程序)公开的驱动器上的其他类型的位置,您可以通过使用 PowerShell 命令(如果可用)避免该问题。当您调用 .NET 方法时,请务必先将所有相对路径解析为绝对的文件系统本机[2]路径 strong> 或者,在支持的情况下,另外提供当前 PowerShell 文件系统位置作为参考目录 - 这可以避免当前目录不匹配的问题。
(另一个但次优的选项是每次传递相对路径时首先设置
[Environment]::CurrentDirectory = $PWD.ProviderPath
,但这很笨拙,不应该在以下情况下使用同一进程中可能存在多个 PowerShell 运行空间。)下一节展示如何安全地将相对 PowerShell 路径传递给 .NET 方法,而底部部分解决您问题中的特定问题:如何解析任意给定的问题绝对、本机文件系统路径的 PowerShell 路径。
警告:
为了解析相对路径,下面的解决方案假定 PowerShell 的当前位置(由
Get-Location
输出/反映在 < code>$PWD) 是一个文件系统位置,即目录 - 这是典型的。如果无法做出此假设(例如,在不太可能发生的情况下,当前位置是注册表位置),则显式引用
文件系统
提供者的位置是必需的,使用<代码>(获取位置-PSProvider FileSystem).ProviderPath 代替下面的$PWD.ProviderPath
。值得注意的是,这排除了下面的Convert-Path
和New-Item
方法。将已知的相对文件系统路径安全地传递给 .NET 方法:
如上所述,当前目录中的差异要求将绝对路径传递给 .NET 方法,到达基于 PowerShell 的当前目录。
这些示例假设相对路径
someFile.txt
传递给 .NET 方法[IO.File]::ReadAllText()
和[IO.File]:: WriteAllText()
请注意,使用了简单的字符串插值,使用
/
(可与\
互换使用)来连接路径组件;如果当前目录恰好是 root 目录,则最终会得到 2 路径分隔符,但这不会影响功能。但是,如果您仍然需要避免这种情况,请改用Join-Path
cmdlet。如果路径已知存在:
使用
Convert-Path
:转换路径
和Resolve-Path
仅有效不幸的是,使用现有路径(从PowerShell(Core)7.2开始); GitHub Issue #2993 中提议为不存在的路径提供选择加入。同样,如果
Convert-Path
和Resolve-Path
支持-PSProvider
参数以允许显式指定目标提供程序,则会很有帮助,如 < code>Get-Location 已支持 - 请参阅 GitHub 问题 #10494。如果路径要创建:
最简单且强大的方法是使用
New-Item
让 PowerShell 预先创建项目,该项目返回一个文件- 系统信息对象.FullName
属性反映完整的本机路径:如果您不想事先在 PowerShell 中创建文件/目录,有几种方法:
$PWD
(如果当前目录基于使用New-PsDrive
创建的 PowerShell 特定驱动器,或者当前位置不是文件系统位置):$PWD.ProviderPath
(将基于 PowerShell 驱动器的路径解析为底层本机文件系统路径,但如果当前位置不是文件系统位置):(Get-Location -PSProvider FileSystem).ProviderPath
解析任何给定的文件系统路径 到一个完整的、原生的一:
也就是说,您可能必须将给定的完整文件系统本机路径解析为相对路径,该路径可能是相对的,也可能不是相对的,并且可能基于也可能不基于PowerShell 特定的驱动器(.NET对此一无所知)。
如果路径存在,请使用
Convert-Path
将任何 PowerShell 文件系统路径解析为文件系统本机的绝对路径:相关的
Resolve-Path
cmdlet 提供类似的功能,但它不会将基于 PowerShell 特定驱动器(使用New-PsDrive
创建)的路径解析到其底层本机文件系统路径。如果路径不存在(尚):
注意:
$PWD.ProviderPath
来引用当前文件-系统位置的本机路径。如前所述,这假定 PowerShell 的当前位置确实是一个文件系统位置,这是典型的情况;为了获得完全的鲁棒性,请改用(Get-Location -PSProvider FileSystem).ProviderPath
。在基于 .NET Core / .NET 5+ 构建的 PowerShell (Core) v6+ 中,您可以使用新的
[IO.Path]::GetFullPath()
重载 <强>接受指定相对路径的参考目录:在Windows PowerShell中,您另外需要
[IO.Path]::Combine( )
:注意:在最简单的情况中,即如果您的相对路径从不以
\
(或/
)开头[ 3] 并且您不关心标准化结果路径(通过解析.\
或..\
组件和/或将/
标准化为\
),[IO.Path]::Combine()
单独就足够了:组合
[ IO.Path]::Combine()
与[IO.Path]::GetFullPath()
克服了这些限制:[1] 虽然给定进程通常只有 一个 PowerShell运行空间(会话),多个进程共存于一个进程中的可能性意味着,从概念上讲,它们不可能将各自的工作目录与唯一的进程范围的 .NET 工作目录同步。如需更深入的说明,请参阅此 GitHub 问题。
[2] 即基于 PowerShell 特定驱动器的路径(请参阅
New-PSDrive
)必须转换为基于所有进程已知的驱动器的路径。[3] 正如 Theo 所说,
[IO.Path]::Combine( )
认为以\
(或/
)开头的(非 UNC)路径是完整路径,尽管仅具有 root 权限相对于当前的开车。。因此,此类路径必须以驱动器规格为前缀。 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.
This has the following implications:
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.When you do call .NET methods, be sure to resolve any relative paths to absolute, file-system-native[2] 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.
(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.)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.
Caveat:
For resolving relative paths, the solutions below assume that PowerShell's current location (as output by
Get-Location
/ reflected in$PWD
) is a file-system location, i.e. a directory - as is typical.If this assumption cannot be made (in the unlikely event that the current location is a registry location, for instance), an explicit reference to the
FileSystem
provider's location is required, using(Get-Location -PSProvider FileSystem).ProviderPath
in lieu of$PWD.ProviderPath
below. Notably, this precludes theConvert-Path
andNew-Item
approaches below.Passing a known relative file-system path safely to a .NET method:
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.
The examples assume relative path
someFile.txt
to be passed to .NET method[IO.File]::ReadAllText()
and[IO.File]::WriteAllText()
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 theJoin-Path
cmdlet instead.If the path is known to exist:
Use
Convert-Path
:That
Convert-Path
andResolve-Path
only work with existing paths (as of PowerShell (Core) 7.2) is unfortunate; providing an opt- in for nonexistent path has been proposed in GitHub issue #2993.Similarly, it would be helpful if
Convert-Path
andResolve-Path
supported a-PSProvider
parameter to allow specifying the target provider explicitly, asGet-Location
already supports - see GitHub issue #10494.If the path is one to be created:
The simplest and robust approach is to use
New-Item
to let PowerShell create the item beforehand, which returns a file-system information object whose.FullName
property reflects the full, native path:If you don't want to create the file/directory in PowerShell beforehand, there are several approaches:
$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):$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):(Get-Location -PSProvider FileSystem).ProviderPath
Resolving any given file-system path to a full, native one:
That is, you may have to resolve to a full, file-system native a path that is given to you, which may or may not be relative, and may or may not be based on a PowerShell-specific drive (which .NET knows nothing about).
If the path exists, use
Convert-Path
to resolve any PowerShell filesystem path to an absolute, filesystem-native one:The related
Resolve-Path
cmdlet provides similar functionality, but it doesn't resolve paths based on PowerShell-specific drives (created withNew-PsDrive
) to their underlying native filesystem paths.If the path doesn't exist (yet):
Note:
$PWD.ProviderPath
is used below to refer to the current file-system location's native path. As noted, this presumes that PowerShell's current location is indeed a file-system location, as is typical; for full robustness, use(Get-Location -PSProvider FileSystem).ProviderPath
instead.In PowerShell (Core) v6+, which builds on .NET Core / .NET 5+, you can use the new
[IO.Path]::GetFullPath()
overload that accepts a reference directory for the specified relative path:In Windows PowerShell, you additionally need
[IO.Path]::Combine()
:Note: In the simplest case, i.e. if your relative paths never start with
\
(or/
)[3] and you don't care about normalizing the resulting path (by resolving.\
or..\
components and/or having/
normalized to\
),[IO.Path]::Combine()
alone is enough:Combining
[IO.Path]::Combine()
with[IO.Path]::GetFullPath()
overcomes these limitations:[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.
[2] That is, paths based on PowerShell-specific drives (see
New-PSDrive
) must be translated to paths based on drives known to all processes.[3] As Theo notes,
[IO.Path]::Combine()
considers a (non-UNC) path that starts with\
(or/
) a full one, despite being rooted only relative to the current drive. Therefore, such paths must be prefixed with the drive spec. of the native file-system directory underlying PowerShell's current location to form a truly full path.这更像是 .net 的事情:为什么不' PowerShell 中的 .NET 对象使用当前目录?
常见的解决方法:
This is more of a .net thing: Why don't .NET objects in PowerShell use the current directory?
A common workaround:
Powershell 将
.
视为当前位置。因此,如果您执行Get-ChildItem -Path ".\"
,这将返回当前目录中的所有内容。要获取相对路径,您需要执行以下操作:$path
现在将是相对路径Powershell treats the
.
as the current location. So if you doGet-ChildItem -Path ".\"
, this will return everything in the current directory. To get a relative path, you would need to do something like this:$path
would now be a relative path