【问题标题】:Powershell: resolve path that might not exist?Powershell:解析可能不存在的路径?
【发布时间】:2014-01-03 19:29:36
【问题描述】:

我正在尝试处理一个文件列表,这些文件可能是最新的,也可能不是最新的,可能存在也可能不存在。这样做时,我需要解析项目的完整路径,即使项目可能使用相对路径指定。但是,Resolve-Path 与不存在的文件一起使用时会打印并出错。

例如,在 Powershell 中将".\newdir\newfile.txt" 解析为"C:\Current\Working\Directory\newdir\newfile.txt" 的最简单、最干净的方法是什么

请注意,System.IO.Path 的静态方法与 进程的 工作目录一起使用 - 这不是 powershell 当前位置。

【问题讨论】:

  • 在 PowerShell 2 和 3 中,您可以使用 Resolve-Path
  • 对于不存在的路径会失败,我正在尝试创建文件,所以这是预期的情况。
  • 这应该是 Resolve-Path 开箱即用的一个特性。它实际上确实解决了它,但随后引发了一个不需要的错误......

标签: powershell


【解决方案1】:

你想要:

c:\path\exists\> $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(".\nonexist\foo.txt")

返回:

c:\path\exists\nonexists\foo.txt

这具有使用 PSPaths 而不是本机文件系统路径的优势。 PSPath 可能不会将 1-1 映射到文件系统路径,例如,如果您使用多字母驱动器名称挂载 psdrive。

什么是 pspath?

ps c:\> new-psdrive temp filesystem c:\temp
...
ps c:\> cd temp:
ps temp:\> 

temp:\ 是一个驱动器限定的 pspath,它映射到 c:\temp 的 win32(本机)路径。

-奥辛

【讨论】:

  • 非常感谢!该命令可能有点冗长,但它正是我正在寻找的。​​span>
  • 天啊,Resolve-Path cmdlet 怎么也没有不测试该路径的标志。
  • @x0n 根据Approved Verbs 页面,所有 Resolve 的意思是“将资源的简写表示映射到更完整的表示”。它没有说明检查资源是否存在。
  • ps>[io.path]::GetFullPath("./somedir") 返回 "D:\bla-bla-folder\somedir"
  • @Vladislav 没那么简单 - 仅适用于 win32 路径,不适用于 powershell 路径(它们是不同的东西)nivot.org/post/2010/03/05/…
【解决方案2】:

Resolve-Path 由于文件不存在而失败时,可以从抛出的错误对象中访问完全解析的路径。

您可以使用如下函数来修复 Resolve-Path 并使其按预期工作。

function Force-Resolve-Path {
    <#
    .SYNOPSIS
        Calls Resolve-Path but works for files that don't exist.
    .REMARKS
        From http://devhawk.net/blog/2010/1/22/fixing-powershells-busted-resolve-path-cmdlet
    #>
    param (
        [string] $FileName
    )

    $FileName = Resolve-Path $FileName -ErrorAction SilentlyContinue `
                                       -ErrorVariable _frperror
    if (-not($FileName)) {
        $FileName = $_frperror[0].TargetObject
    }

    return $FileName
}

【讨论】:

  • 恕我直言,迄今为止最好的答案
  • 为什么需要“$FileName = $_frperror[0].TargetObject”?为什么不默默地继续呢?
  • @Rookian 因为如果路径不存在,ResolvePath 返回$null
【解决方案3】:

我认为你走在正确的道路上。只需使用 [Environment]::CurrentDirectory 来设置 .NET 对进程当前目录的概念,例如:

[Environment]::CurrentDirectory = $pwd
[IO.Path]::GetFullPath(".\xyz")

【讨论】:

  • 是的,但这有副作用和一些我想避免的讨厌的互操作 - 我有点希望这会是 powershell (这是一种 shell 脚本语言,毕竟)可以原生处理...无论如何,+1 为实际选项!
  • 它还有一个问题,即 $pwd 实际上也可能不是文件系统上的真实路径;例如,如果您使用多字母驱动器名称安装 psdrive。
  • 好点。当然,您可以先获取 .NET 当前目录,然后将其设置为文件系统提供程序路径(不使用 $pwd),然后将当前目录重置为其原始值。
【解决方案4】:
Join-Path (Resolve-Path .) newdir\newfile.txt

【讨论】:

  • 您还可以在documentation中找到更多示例
  • 仅在假设 'newdir' 是相对的情况下才有效。 Join-Path (Resolve-Path .) F:\thisuer\specified\absolutepath\inparameter
【解决方案5】:

这样做的好处是不必设置 CLR 环境的当前目录:

[IO.Path]::Combine($pwd,"non\existing\path")

注意

这在功能上不等同于x0n's answerSystem.IO.Path.Combine 仅组合字符串路径段。它的主要用途是让开发人员不必担心斜线。 GetUnresolvedProviderPathFromPSPath 将根据... 遍历相对于当前工作目录的输入路径。

【讨论】:

  • 好短!这是否完全等同于基于GetUnresolvedProviderPathFromPSPath 的解决方案,还是有细微差别?
  • 不是一个真正可行的解决方案。它假定 psdrive 与提供程序后备存储具有相同的名称。您可以在 powershell 中有一个名为“stuff:\”的驱动器,它映射到“c:\”,例如,此解决方案将传递 psdrive 名称而不是会失败的 win32 名称
  • @x0n 感谢您提出这个有趣的警告。
【解决方案6】:

我发现以下方法已经足够好了。

$workingDirectory = Convert-Path (Resolve-Path -path ".")
$newFile = "newDir\newFile.txt"
Do-Something-With "$workingDirectory\$newFile"

Convert-Path 可用于将路径作为字符串获取,尽管并非总是如此。有关详细信息,请参阅 COnvert-Path 上的 this 条目。

【讨论】:

    【解决方案7】:
    function Get-FullName()
    {
        [CmdletBinding()]
        Param(
            [Parameter(ValueFromPipeline = $True)] [object[]] $Path
        )
        Begin{
            $Path = @($Path);
        }
        Process{
            foreach($p in $Path)
            {
                if($p -eq $null -or $p -match '^\s*$'){$p = [IO.Path]::GetFullPath(".");}
                elseif($p -is [System.IO.FileInfo]){$p = $p.FullName;}
                else{$p = [IO.Path]::GetFullPath($p);}
                $p;
            }
        }
    }
    

    【讨论】:

    • 这仅适用于使用 psdrive 名称与支持驱动器名称相同的情况。也仅适用于 win32 文件路径,并且在所有其他提供程序上都将失败。
    【解决方案8】:

    在我的情况下,我最终得到了这段代码。我需要稍后在脚本中创建一个文件,因此此代码假定您对目标文件夹具有写入权限。

    $File = ".\newdir\newfile.txt"
    If (Test-Path $File) {
        $Resolved = (Resolve-Path $File).Path
    } else {
        New-Item $File -ItemType File | Out-Null
        $Resolved = (Resolve-Path $File).Path
        Remove-Item $File
    }
    

    我还在try..catch 块中附上了New-Item,但这不属于这个问题。

    【讨论】:

      【解决方案9】:

      我有一个类似的问题,我需要从一个尚不存在的文件夹中找到 3 级的文件夹,以确定我想要创建的新文件夹的名称......这很复杂。无论如何,这就是我最终要做的:

      ($path -split "\\" | select -SkipLast 3) -join "\\"
      

      【讨论】:

        【解决方案10】:

        您可以将 -errorAction 设置为“SilentlyContinue”并使用 Resolve-Path

        5 >  (Resolve-Path .\AllFilerData.xml -ea 0).Path
        C:\Users\Andy.Schneider\Documents\WindowsPowerShell\Scripts\AllFilerData.xml
        
        6 >  (Resolve-Path .\DoesNotExist -ea 0).Path
        
        7 >
        

        【讨论】:

        • 将错误操作设置为 0 并不能真正解决解决路径无法解决该(完全有效)路径的问题。
        【解决方案11】:

        这里有一个公认的答案,但它很长,还有一个更简单的替代方案。

        在任何最新版本的 Powershell 中,您都可以使用Test-Path -IsValid -Path 'C:\Probably Fake\Path.txt'

        这只是验证路径中没有非法字符,并且该路径可用于存储文件。如果目标不存在,Test-Path 在这种情况下不会关心——它只会被要求测试提供的路径是否可能有效。

        【讨论】:

        • 测试路径的有效性;我想将其解析为绝对路径(例如,出于规范化目的,或者能够存储路径并在具有不同环境的另一个上下文中重用它)。
        【解决方案12】:

        两个最流行的答案在不存在的驱动器上的路径上都不能正常工作。

        function NormalizePath($filename)
        {
            $filename += '\'
            $filename = $filename -replace '\\(\.?\\)+','\'
            while ($filename -match '\\([^\\.]|\.[^\\.]|\.\.[^\\])[^\\]*\\\.\.\\') {
                $filename = $filename -replace '\\([^\\.]|\.[^\\.]|\.\.[^\\])[^\\]*\\\.\.\\','\'
            }
            return $filename.TrimEnd('\')
        }
        

        【讨论】:

        • 如果您解释一下这是做什么的,将会很有帮助。乍一看,这并不是一件显而易见的事情,对于不精通正则表达式的人来说更糟。此外,当我将问题中的示例路径传递给此函数时,它只会返回相同的路径。传递'C:\' 返回'C:',这意味着不同的东西。我得到了'C:\Dir1\..\File1' 的正确结果,但'Dir1\..\File1' 未经修改返回,'Dir1\Dir2\..\File1' 只是变成'Dir1\File1',而不是绝对路径。这是否打算在这些答案的解决方案之一之前使用?
        • 另外:通过正则表达式操作结构化字符串是出了名的棘手。像这样的解决方案可能很容易出现令人讨厌的极端情况;看起来@BACON 已经找到了一些。即使你能正确,也几乎不可能验证它是正确的;一个巨大的测试集将是一个好的开始。
        【解决方案13】:

        解析前检查文件是否存在:

        if(Test-Path .\newdir\newfile.txt) { (Resolve-Path .\newdir\newfile.txt).Path }
        

        【讨论】:

        • 我需要解析现有和不存在的文件;我将创建不存在的。
        猜你喜欢
        • 2012-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多