System.IO.FileInfo 和相对路径

问题描述 投票:0回答:3

我想知道是否有人可以帮助我理解为什么

System.IO.FileInfo
在处理相对路径时在 Windows 上的行为与在 Linux 上的行为不同。

示例

  • 在 Linux 上
PS /home/user/Documents> ([System.IO.FileInfo]'./test.txt').FullName
/home/user/Documents/test.txt
  • 在 Windows 上
PS C:\Users\User\Documents> ([System.IO.FileInfo]'.\test.txt').FullName
C:\Users\User\test.txt

编辑

澄清上述内容,

System.IO.FileInfo
在 Windows 或 Linux 上处理相对路径的方式没有区别。该问题与
[System.IO.Directory]::GetCurrentDirectory()
未由
Push-Location
Set-Location
更新有关。

一个简单的例子:

PS /home/user> [System.IO.Directory]::GetCurrentDirectory()
/home/user
PS /home/user> cd ./Documents/
PS /home/user/Documents> [System.IO.Directory]::GetCurrentDirectory()
/home/user

假设这是预期的行为,那么处理脚本和函数上的

param(...)
块以接受两种情况(绝对和相对)的最佳方法是什么。我曾经将路径参数约束为
System.IO.FileInfo
但现在我可以看到它显然是错误的。

这是我遇到的,但我想知道是否有更好的方法。
我相信

Split-Path -IsAbsolute
如果使用网络路径也会带来问题,如果我错了请纠正我。

param(
    [ValidateScript({ 
        if(Test-Path $_ -PathType Leaf) {
            return $true
        }
        throw 'Invalid File Path'
    })]
    [string] $Path
)

if(-not (Split-Path $Path -IsAbsolute)) {
    [string] $Path = Resolve-Path $Path
}
powershell system.io.fileinfo
3个回答
2
投票

感觉有点重复,但既然你问了..

抱歉我不了解 Linux,但在 Windows 中:

您可以先添加一个测试来查看路径是否是相对路径,如果是,则将其转换为绝对路径,例如:

$Path = '.\test.txt'
if (![System.IO.Path]::IsPathRooted($Path) -or $Path -match '^\\[^\\]+') {
    $Path =  [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($pwd, $Path))
}

我添加了

$Path -match '^\\[^\\]+'
来转换以反斜杠开头的相对路径,如
\ReadWays.ps1
意味着路径从根目录开始。以两个反斜杠开头的 UNC 路径被视为绝对路径。


显然(我真的不知道为什么..)上面的内容在Linux上不起作用,因为在那里,当使用UNC路径时,部分

![System.IO.Path]::IsPathRooted('\\server\folder')
产生
True

看来你需要先检查操作系统,并在 Linux 上进行不同的检查。

$Path = '\\server\share'

if ($IsWindows) {  # $IsWindows exists in version 7.x. Older versions do `$env:OS -match 'Windows'`
    if (![System.IO.Path]::IsPathRooted($Path) -or $Path -match '^\\[^\\]+') {
        $Path =  [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($pwd, $Path))
    }
}
else {
    if ($Path -notlike '\\*\*') {  # exclude UNC paths as they are not relative
        if (![System.IO.Path]::IsPathRooted($Path) -or $Path -match '^\\[^\\]+') {
            $Path =  [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($pwd, $Path))
        }
    }
}

2
投票

另一种选择:

当您想要将结果转换为

[System.IO.FileInfo]
时,您可以使用
Get-Item
,它也将返回
[System.IO.FileInfo]
对象,但具有解析的相对路径 如预期。它还将包含一些错误检测(无效字符或不存在的路径等)。

示例:

PS C:\Users\User\Documents> (Get-Item -LiteralPath '.\test.txt').FullName
C:\Users\User\Documents\test.txt

2
投票

最简单的替代方法是使用

Convert-Path
来:

  • 处理 UNC、相对、绝对和根路径。
  • 兼容Windows和Linux
  • 高效

如果我们使用

[cmdletbinding()]
,另一个巧妙的选择是使用
$PSCmdlet.GetUnresolvedProviderPathFromPSPath(..)
方法
:

function Test-ResolvePath {
    [cmdletbinding()]
    param($path)
    $PSCmdlet.GetUnresolvedProviderPathFromPSPath($path)
}

Test-ResolvePath \\server01\test         # => \\server01\test
Test-ResolvePath C:\Users\user\Documents # => C:\Users\user\Documents
Test-ResolvePath C:Documents             # => C:\Documents
(Test-ResolvePath .) -eq $PWD.Path       # => True
(Test-ResolvePath ~) -eq $HOME           # => True

如果我们希望模拟大多数 Microsoft.PowerShell.Management cmdlet 的行为,具有

-LiteralPath
-Path
,则代码会更复杂一些,并且调用的 API 也不同,
PathIntrinsics.GetUnresolvedProviderPathFromPSPath 
method
用于文字路径验证,
PSCmdlet.GetResolvedProviderPathFromPSPath
method
用于通配符路径验证。两种方法都确保目标存在。

function Test-ResolvePath {
    [cmdletbinding(DefaultParameterSetName = 'Path')]
    param(
        [Parameter(ParameterSetName = 'LiteralPath', Mandatory, Position = 0)]
        [Alias("PSPath")]
        [string] $LiteralPath,

        [Parameter(ParameterSetName = 'Path', Mandatory, Position = 0)]
        [SupportsWildcards()]
        [string] $Path
    )

    $provider = $null

    try {
        if ($PSCmdlet.ParameterSetName -eq 'LiteralPath') {
            $resolvedPath = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
                $LiteralPath,
                [ref] $provider,
                [ref] $null)
        }
        else {
            $resolvedPath = $PSCmdlet.GetResolvedProviderPathFromPSPath(
                $Path,
                [ref] $provider)
        }

        foreach ($path in $resolvedPath) {
            [pscustomobject]@{
                Path             = $path
                Provider         = $provider
                IsFileSystemPath = $provider.ImplementingType -eq [Microsoft.PowerShell.Commands.FileSystemProvider]
            }
        }
    }
    catch {
        $PSCmdlet.WriteError($_)
    }
}

Test-ResolvePath $pwd\*
Test-ResolvePath HKLM:
Test-ResolvePath -LiteralPath Cert:
Test-ResolvePath doesnotexist
© www.soinside.com 2019 - 2024. All rights reserved.