Answers:
您可以使用的组合pwd
,Join-Path
并[System.IO.Path]::GetFullPath
得到一个完全合格的扩展的路径。
由于cd
(Set-Location
)不会更改进程的当前工作目录,因此只需将相对文件名传递给无法理解PowerShell上下文的.NET API,可能会产生意想不到的副作用,例如,基于初始工作解析为路径目录(不是您当前的位置)。
您要做的是首先确定自己的道路:
Join-Path (Join-Path (pwd) fred\frog) '..\frag'
这产生了(鉴于我当前的位置):
C:\WINDOWS\system32\fred\frog\..\frag
在绝对基础上,可以安全地调用.NET API GetFullPath
:
[System.IO.Path]::GetFullPath((Join-Path (Join-Path (pwd) fred\frog) '..\frag'))
这为您提供了完全合格的路径,并且已..
删除:
C:\WINDOWS\system32\fred\frag
就我个人而言,它也不复杂,我不屑于依赖于外部脚本的解决方案,这是一个简单的问题,用Join-Path
and 相当恰当地解决了pwd
(GetFullPath
只是为了使其美观)。如果只想保留相对部分,只需添加.Substring((pwd).Path.Trim('\').Length + 1)
,瞧!
fred\frag
感谢@Dangph指出了C:\
边缘情况。
cd c:\; "C:\fred\frag".Substring((pwd).Path.Length + 1)
。没什么大不了的; 只是要注意的事情。
cd c:\; "C:\fred\frag".Substring((pwd).Path.Trim('\').Length + 1)
。虽然有点长。
您可以使用resolve-path将.. \ frag扩展到其完整路径:
PS > resolve-path ..\frag
尝试使用Combine()方法规范化路径:
[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
C:\Windows
与C:\Windows\
相同的路径却有两个不同的结果该怎么办
[io.path]::Combine
相反。更好的是,使用本机Join-Path
PowerShell命令:Join-Path (Resolve-Path ..\frag).Path 'fred\frog'
另外请注意,至少从PowerShell v3开始,Resolve-Path
现在支持-Relative
用于解析到相对于当前文件夹的路径的开关。如前所述,Resolve-Path
与相比,仅适用于现有路径[IO.Path]::GetFullPath()
。
您也可以使用Path.GetFullPath,尽管(与Dan R的答案一样)这将为您提供完整的路径。用法如下:
[IO.Path]::GetFullPath( "fred\frog\..\frag" )
或更有趣的是
[IO.Path]::GetFullPath( (join-path "fred\frog" "..\frag") )
两者都会产生以下内容(假设您的当前目录为D:\):
D:\fred\frag
请注意,此方法不会尝试确定fred或frag是否实际存在。
[System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath))
[IO.Path]::GetFullPath()
与PowerShell的本机不同Resolve-Path
,它也适用于不存在的路径。@JasonMArcher指出,这样做的缺点是需要首先将.NET的工作文件夹与PS同步。
Join-Path
如果引用的驱动器不存在,则会导致异常。
公认的答案是一个很大的帮助,但是它也不能正确地“标准化”绝对路径。在我的派生工作下面查找可归一化绝对路径和相对路径的导数。
function Get-AbsolutePath ($Path)
{
# System.IO.Path.Combine has two properties making it necesarry here:
# 1) correctly deals with situations where $Path (the second term) is an absolute path
# 2) correctly deals with situations where $Path (the second term) is relative
# (join-path) commandlet does not have this first property
$Path = [System.IO.Path]::Combine( ((pwd).Path), ($Path) );
# this piece strips out any relative path modifiers like '..' and '.'
$Path = [System.IO.Path]::GetFullPath($Path);
return $Path;
}
[IO.Path]::GetFullPath()
,无法正确确定纯文件名的目录。
任何非PowerShell路径操作功能(例如System.IO.Path中的功能)都无法从PowerShell可靠,因为PowerShell的提供程序模型允许PowerShell的当前路径与Windows认为该进程的工作目录不同。
另外,您可能已经发现,PowerShell的Resolve-Path和Convert-Path cmdlet可用于将相对路径(包含'..'的相对路径)转换为驱动器限定的绝对路径,但是如果引用的路径不存在,它们将失败。
以下非常简单的cmdlet应该适用于不存在的路径。即使找不到“ fred”或“ frag”文件或文件夹(当前的PowerShell驱动器为“ d:”),它也会将“ fred \ frog \ ... frag”转换为“ d:\ fred \ frag” 。
function Get-AbsolutePath {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string[]]
$Path
)
process {
$Path | ForEach-Object {
$PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
}
}
}
Get-AbsolutePath q:\foo\bar\..\baz
即使是有效路径也失败。好吧,这取决于您对有效路径的定义。:-) FWIW,即使内置Test-Path <path> -IsValid
驱动器在以不存在的驱动器为根的路径上也失败。
HKLM:\SOFTWARE
是PowerShell中的有效路径,是指SOFTWARE
本地计算机注册表配置单元中的键。但是要弄清楚它是否有效,就需要弄清楚注册表路径的规则是什么。
这个库很好:NDepend.Helpers.FileDirectoryPath。
编辑:这是我想出的:
[Reflection.Assembly]::LoadFrom("path\to\NDepend.Helpers.FileDirectoryPath.dll") | out-null
Function NormalizePath ($path)
{
if (-not $path.StartsWith('.\')) # FilePathRelative requires relative paths to begin with '.'
{
$path = ".\$path"
}
if ($path -eq '.\.') # FilePathRelative can't deal with this case
{
$result = '.'
}
else
{
$relPath = New-Object NDepend.Helpers.FileDirectoryPath.FilePathRelative($path)
$result = $relPath.Path
}
if ($result.StartsWith('.\')) # remove '.\'.
{
$result = $result.SubString(2)
}
$result
}
这样称呼它:
> NormalizePath "fred\frog\..\frag"
fred\frag
请注意,此代码段需要DLL的路径。您可以使用一种技巧来找到包含当前正在执行的脚本的文件夹,但是在我的情况下,我有一个可以使用的环境变量,因此我只是使用了它。
这给出了完整的路径:
(gci 'fred\frog\..\frag').FullName
这给出了相对于当前目录的路径:
(gci 'fred\frog\..\frag').FullName.Replace((gl).Path + '\', '')
由于某些原因,它们仅在frag
是文件而不是时才起作用directory
。
创建一个函数。此功能将规范化系统上不存在的路径,并且不添加驱动器号。
function RemoveDotsInPath {
[cmdletbinding()]
Param( [Parameter(Position=0, Mandatory=$true)] [string] $PathString = '' )
$newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', ''
return $newPath
}
例如:
$a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt'
RemoveDotsInPath $a
'fooA\bin\BusinessLayer\foo.txt'
感谢Oliver Schadlich在RegEx中提供的帮助。
somepaththing\.\filename.txt
保留单个点这样的路径不起作用
如果路径包含限定符(驱动器号),则x0n对Powershell的回答:解析可能不存在的路径?将规范化路径。如果该路径不包含限定符,则仍将对其进行规范化,但将返回相对于当前目录的完全限定路径,这可能不是您想要的。
$p = 'X:\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
X:\fred\frag
$p = '\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\fred\frag
$p = 'fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\Users\WileCau\fred\frag
这里的注释的权宜之计结合起来,使它们统一了相对路径和绝对路径:
[System.IO.Directory]::SetCurrentDirectory($pwd)
[IO.Path]::GetFullPath($dapath)
一些样本:
$fps = '.', 'file.txt', '.\file.txt', '..\file.txt', 'c:\somewhere\file.txt'
$fps | % { [IO.Path]::GetFullPath($_) }
输出:
C:\Users\thelonius\tests
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\file.txt
c:\somewhere\file.txt