System.IO.FileInfo
在处理相对路径时在 Windows 上的行为与在 Linux 上的行为不同。
PS /home/user/Documents> ([System.IO.FileInfo]'./test.txt').FullName
/home/user/Documents/test.txt
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
}
感觉有点重复,但既然你问了..
抱歉我不了解 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))
}
}
}
另一种选择:
当您想要将结果转换为
[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
Convert-Path
来:
[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