如何规范PowerShell中的path?
我有两条path:
fred\frog
和
..\frag
我可以像这样在PowerShell中一起join:
join-path 'fred\frog' '..\frag'
这给了我这个:
fred\frog\..\frag
但我不想那样 我想要一个没有双点的标准化path,就像这样:
fred\frag
我怎么能得到这个?
您可以使用pwd
, Join-Path
和[System.IO.Path]::GetFullPath
来获取完全限定的扩展path。
由于cd
( Set-Location
)不改变进程当前的工作目录,简单地传递一个相对的文件名到一个不理解PowerShell上下文的.NET API,可能会产生意想不到的副作用,例如parsing为基于pathclosures最初的工作目录(不是你目前的位置)。
你做什么是你第一次限定你的path:
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'))
这给你完全合格的path和..
删除:
C:\WINDOWS\system32\fred\frag
这并不复杂,个人而言,我蔑视依赖于外部脚本的解决scheme,这是一个简单的问题,相当适合通过Join-Path
和pwd
( GetFullPath
只是为了让它变得漂亮)解决。 如果你只想保留相对的部分 ,你只需添加.Substring((pwd).Path.Trim('\').Length + 1)
,瞧!
fred\frag
UPDATE
感谢@Dangph指出了C:\
边界情况。
您可以使用resolve-path将.. \ frag扩展为完整path:
PS > resolve-path ..\frag
尝试使用combine()方法来标准化path:
[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
你也可以使用Path.GetFullPath ,虽然(和Dan R的答案一样),这会给你整个path。 用法如下:
[IO.Path]::GetFullPath( "fred\frog\..\frag" )
或者更有趣
[IO.Path]::GetFullPath( (join-path "fred\frog" "..\frag") )
这两个产生以下(假设您的当前目录是D:\):
D:\fred\frag
请注意,此方法不会尝试确定fred或frag是否实际存在。
接受的答案是一个很大的帮助,但是它不能正常地“规范”一个绝对path。 在下面find我的派生工作,使绝对和相对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; }
PowerShell的任何非PowerShellpath操作函数(如System.IO.Path中的那些函数)都不可靠,因为PowerShell的提供程序模型允许PowerShell的当前path与Windows认为进程的工作目录不同。
此外,正如您可能已经发现的那样,PowerShell的Resolve-Path和Convert-Path cmdlet对于将相对path(包含'..'的path)转换为驱动器限定的绝对path很有用,但如果引用的path不存在,则会失败。
以下非常简单的cmdlet应该适用于不存在的path。 即使无法find'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($_) } } }
这个库很好: 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的path。 有一个技巧可以用来find包含当前正在执行的脚本的文件夹,但在我的情况下,我有一个我可以使用的环境variables,所以我只是用它。
创build一个函数。 此function将标准化系统中不存在的path,也不会添加驱动器号。
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的帮助。
这给出了完整的path:
(gci 'fred\frog\..\frag').FullName
这给出了相对于当前目录的path:
(gci 'fred\frog\..\frag').FullName.Replace((gl).Path + '\', '')
出于某种原因,只有在frag
是文件而不是directory
,它们才能工作。
那么,一种方法是:
Join-Path 'fred\frog' '..\frag'.Replace('..', '')
等等,也许我误解了这个问题。 在你的例子中,frag是一个青蛙的子文件夹吗?
如果你需要摆脱..部分,你可以使用System.IO.DirectoryInfo对象。 在构造函数中使用'fred \ frog .. \ frag'。 FullName属性将为您提供规范化的目录名称。
唯一的缺点是它会给你整个path(例如c:\ test \ fred \ frag)。