0

我编写了一个脚本来创建一系列符号链接。我想将目标值设置为 $shortpath 其中

$shortpath = "%userprofile%\dir1\dir2\dir3\filename.ext"

$shortpath 变量的值是有效的,我可以从运行命令打开它。PS 在创建符号链接时尝试写入的字符串与预期的不同。我希望它会写入字符串的值,或者至少插入 Env 变量的值。而是添加到我传递给它的字符串中。

New-Item -Path $currpath -ItemType SymbolicLink -Value ($targetpath) -Force

我希望目标值为: c:\Users\UserName\dir1\dir2\dir3\filename.ext 或 %userprofile%\dir1\dir2\dir3\filename.ext

相反,我得到: C:\windows\system32%UserProfile$\dir1\dir2\dir3\filename.ext

写入日志文件的输出示例:

wholepath = C:\Users\UserName\dir1\dir2\dir3\longfilename1.ext
spath = C:\Users\UserName\dir1\dir2\dir3\longfi~1.ext
envpath = C:\Users\UserName\
midpart = dir1\dir2\dir3\
filename = longfi~1.ext
targetpath = %UserProfile%\dir1\dir2\dir3\longfi~1.ext

谁能阐明为什么会发生这种情况?如果我使用 mklink,也会发生同样的事情。我在下面添加了整个脚本:


function Get-ShortPathName
{
    Param([string] $path)

    $MethodDefinition = @'
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetShortPathNameW", SetLastError = true)]
public static extern int GetShortPathName(string pathName, System.Text.StringBuilder shortName, int cbShortName);
'@

    $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru
    $shortPath = New-Object System.Text.StringBuilder(500)
    $retVal = $Kernel32::GetShortPathName($path, $shortPath, $shortPath.Capacity)
    return $shortPath.ToString()

    }

    $logfile="C:\SwSetup\SymLinkScript\log.txt"
    
    <#paths to orignials and place to copy to#>
    $src = $env:userprofile + "\Firestone Technical Resources, Inc\Firestone Technical Resources, Inc Team Site - Documents\Danielle"
    $dest = "C:\SwSetup\asdfg\"
    $src = $src.Replace("\","\\")


    <#  grab the root object, and its children, from the src#>
    $i = Get-ChildItem -LiteralPath $src -Recurse

    <# recurse through the root and process, for lack of a better term, each object#>
    $i | ForEach-Object { 
        Process {
            $apath = $_.FullName -Replace $src,""
            $cpath = $dest + $apath

            <# Create Directory if it does not exist#>
            If(!(Test-Path (Split-Path -Parent $cpath))){
                New-Item -ItemType Directory -Force -Path (Split-Path -Parent $cpath)
            }
            
            <#
            Create the SymLink if it does not exist
            mklink syntax         | PowerShell equivalent       
            mklink /D Link Target | new-item -path <path to location> -itemtype symboliclink -name <the name> -value <path to target>
            #>
            If(!$_.PSIsContainer){
                If(!(Get-Item $cpath -ErrorAction SilentlyContinue)){
                    <#establish 8.3path#>
                    $wholepath = ([WildcardPattern]::Escape($_.FullName))
                    $shortPath = Get-ShortPathName($wholepath)
                    $envpath = $shortpath.substring(0,18)
                    $midpart = ((Split-path $shortpath -parent).trimstart($envpath)) +"\"
                    $filename = Split-Path $shortpath -leaf
                    $targetpath = "%UserProfile%\" + $midpart + $filename
                    
                    <#write to log file#>
                    "wholepath = " + $wholepath >> $logfile
                    "spath = " + $Shortpath >>$logfile
                    "envpath = " + $envpath >> $logfile
                    "midpart = " +$midpart >>$logfile
                    "filename = " + $filename >> $logfile
                    "targetpath = " + $targetpath >> $logfile
                    "cpath = " + [string]$cpath >> $logfile
                    "----------" >>$logfile
                    " " >>$logfile
                    
                    <#create symlink#>
                    New-Item -Path $cpath -ItemType SymbolicLink -Value ($targetpath) -Force

                    <#cmd /c mklink $cpath $targetpath#>

                    <#create shortcut
                    $WshShell = New-Object -comObject WScript.Shell
                    $Shortcut = $WshShell.CreateShortcut($targetpath.substring(0,$targetpath.Length-4) + '.lnk')
                    $Shortcut.TargetPath = $targetpath
                    $Shortcut.Save()#>
                }
            }
        }
    }
4

2 回答 2

0

首先,您设置了一个名为的变量$shortpath

$shortpath = "%userprofile%\dir1\dir2\dir3\filename.ext"

然后你说:

New-Item -Path $currpath -ItemType SymbolicLink -Value ($targetpath) -Force

我希望目标值为: c:\Users\UserName\dir1\dir2\dir3\filename.ext 或 %userprofile%\dir1\dir2\dir3\filename.ext

未满足您的期望的原因是您的New-Item行没有引用您的$shortpath变量。

于 2022-02-28T21:27:22.320 回答
0
  • 快捷方式文件( .lnk)在其属性中 确实支持cmd.exe-style 环境变量引用 (eg%USERPROFILE%),但有一个警告

    • 当使用 WshShell COM 对象的API创建或修改快捷方式文件时:.CreateShortcut()

      • 例如,分配属性值.TargetPath = '%USERPROFILE%\Desktop'按预期工作 - 即,字符串按原样存储,对环境变量的引用%USERPROFILE%在运行时扩展,即打开快捷方式文件时。

      • 但是,通过此 COM API查询此类属性也会执行扩展,因此您将无法获取原始定义,并且无法区分.TargetPath包含属性的属性%USERPROFILE%\Desktop,例如逐字记录 C:\Users\jdoe\Desktop

    • .lnk如果对给定文件的实际属性值有疑问,请通过File Explorer检查它。

  • 符号链接(symlinks)没有

符号链接的目标必须指定为文字路径,在您的情况下,这意味着使用PowerShell 的环境变量语法(例如,$env:UserProfile),以便预先将变量引用解析为其

# This results in a *literal* path, because $env:UserProfile is
# instantly resolved; the result can be used with New-Item / mklink
$literalTargetPath = "$env:UserProfile\" + $midpart + $filename

将类似的东西传递'%USERPROFILE%\Desktop'-Value/-Target参数New-Item -Type SymbolicLink因此不起作用按预期),但症状取决于您正在运行的 PowerShell 版本:

  • 您正在使用的Windows PowerShell尝试预先验证目标路径的存在,因此无法创建 symlink,因为它将%USERPROFILE%\Desktop 逐字解释为相对路径(相对于当前目录,这就是为什么后者的path 是前置的),它不存在。

  • PowerShell (Core) 7+也解释了路径verbatim,但它允许使用尚不存在的目标创建符号链接,因此符号链接创建本身会成功。但是,尝试使用生成的符号链接会失败,因为它的目标不存在。

于 2022-02-28T21:41:39.410 回答