我想我在PS中发现了一个错误。
我创建了一个新的子驱动器号:
C:> subst k: c:test
C:> subst
K:: => c:test
但PS告诉:
PS C:> get-item 'K:' | Format-list | Out-String
Directory:
Name : K:
Mode : d-----
LinkType :
Target : {K:test}
正如您所看到的,target中的驱动器号是错误的。它是怎么来的?
我的windows版本:
Windows 10 Enterprise
Version 1809
OS Build 17763.1457
我的PS版本:
PS C:> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.17763.1432
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.17763.1432
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
如何使用ps获得正确的目标?
非常感谢
我同意这是一个错误,但是:
代码中包含无符号链接或其他NTFS重分析点(如连接(。
因此,报告重分析点的目标的
.Target
属性甚至不应该在中填写;这是实际的错误,它在PowerShell〔Core〕v6+中已不再存在。
因此,为了剔除此类错误的.Target
值,您可以通过其.LinkType
属性来过滤文件:
Get-ChildItem | Where-Object LinkType -eq SymbolicLink # now, .Targets are valid
另外,如果您正在寻找将基于替代驱动器的路径转换为其底层物理路径的方法:
不幸的是,Convert-Path
和Get-PSDrive
似乎都不知道替换的驱动器(使用subst.exe
创建(-甚至在PowerShell 7.0中也不知道-所以您必须滚动自己的翻译命令:
& {
$fullName = Convert-Path -LiteralPath $args[0]
$drive = Split-Path -Qualifier $fullName
if ($drive.Length -eq 2 -and ($substDef = @(subst.exe) -match "^$drive")) {
Join-Path ($substDef -split ' ', 3)[-1] $fullName.Substring($drive.Length)
} else {
$fullName
}
} 'K:'
在您的情况下,以上应返回C:test
。
注意:由于使用Convert-Path
,以上仅适用于现有路径;使其支持不存在的路径需要大量的工作(见下文(
请注意,长期存在的GitHub功能请求#2993要求增强Convert-Path
,使其也能处理不存在的路径。
在此期间,这里有高级功能Convert-PathEx
来填补空白。
一旦定义好,您可以执行以下操作:
PS> Convert-PathEx K:
C:test
function Convert-PathEx {
<#
.SYNOPSIS
Converts file-system paths to absolute, native paths.
.DESCRIPTION
An enhanced version of Convert-Path, which, however only supports *literal* paths.
For wildcard expansion, pipe from Get-ChildItem or Get-Item.
The enhancements are:
* Support for non-existent paths.
* On Windows, support for translating paths based on substituted drives
(created with subst.exe) to physical paths.
#>
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('PSPath', 'LP')]
[string[]] $LiteralPath
)
begin {
$isWin = $env:OS -eq 'Windows_NT'
# Helper function for ignoring .Substring() exceptions.
function fromPos ($str, $ndx) {
try { return $str.Substring($ndx) } catch { return '' }
}
}
process {
foreach ($path in $LiteralPath) {
$path = $path -replace '^.+::' # strip any PS provider prefix, such as 'FileSystem::' or 'Microsoft.PowerShell.CoreFileSystem::'
# Analyze drive information.
$driveSpec = Split-Path -ErrorAction Ignore -Qualifier $path
$driveObj = if ($driveSpec) { (Get-PSDrive -ErrorAction Ignore -PSProvider FileSystem -Name $driveSpec.Substring(0, $driveSpec.Length - 1)) | Select-Object -First 1 } # !! Get-PSDrive can report *case-sensitive variations* of the same drive, so we ensure we only get *one* object back.
if ($driveSpec -and -not $driveObj) {
Write-Error "Path has unknown file-system drive: $path" -Category InvalidArgument
continue
}
$rest = if ($driveObj) { fromPos $path $driveSpec.Length } else { $path }
$startsFromRoot = $rest -match '^[\/]'
if ($startsFromRoot) { $rest = fromPos $rest 1 } # Strip the initial separator, so that [IO.Path]::Combine() works correctly (with an initial "" or "/", it ignores attempts to prepend a drive).
$isAbsolute = $startsFromRoot -and ($driveObj -or -not $isWin) # /... paths on Unix are absolute paths.
$fullName =
if ($isAbsolute) {
if ($driveObj) {
# Prepend the path underlying the drive.
[IO.Path]::Combine($driveObj.Root, $rest)
} else {
# Unix: Already a full, native path - pass it through.
$path
}
} else {
# Non-absolute path, which can have one three forms:
# relative: "foo", "./foo"
# drive-qualified relative (rare): "c:foo"
# Windows drive-qualified relative (rare): "c:foo"
if ($startsFromRoot) {
[IO.Path]::Combine($PWD.Drive.Root, $rest)
} elseif ($driveObj) {
# drive-qualified relative path: prepend the current dir *on the targeted drive*.
# Note: .CurrentLocation is the location relative to the drive root, *wihtout* an initial "" or "/"
[IO.Path]::Combine($driveObj.Root, $driveObj.CurrentLocation, $rest)
} else {
# relative path, prepend the provider-native $PWD path.
[IO.Path]::Combine($PWD.ProviderPath, $rest)
}
}
# On Windows: Also check if the path is defined in terms of a
# *substituted* drive (created with `subst.exe`) and translate
# it to the underlying path.
if ($isWin) {
# Note: [IO.Path]::GetPathRoot() only works with single-letter drives, which is all we're interested in here.
# Also, it *includes a trailing separator*, so skipping the length of $diveSpec.Length works correctly with [IO.Path]::Combine().
$driveSpec = [IO.Path]::GetPathRoot($fullName)
if ($driveSpec -and ($substDef = @(subst.exe) -like "$driveSpec*")) {
$fullName = [IO.Path]::Combine(($substDef -split ' ', 3)[-1], (fromPos $fullName $driveSpec.Length))
}
}
# Finally, now that we have a native path, we can use [IO.Path]::GetFullPath() in order
# to *normalize* paths with components such as "./" and ".."
[IO.Path]::GetFullPath($fullName)
} # foreach
}
}