Home > OS >  unexpected behavior when creating a path with %userProfile% environment variable in pwershell
unexpected behavior when creating a path with %userProfile% environment variable in pwershell

Time:03-01

I've written a script to create a series of symbolic links. I want to set the target value to $shortpath where

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

The value of the $shortpath variable is valid and I can open it from the run command. The string that PS is trying to write at the creation of the symlink is different than anticipated. I expect that it would write the value of the string, or at least insert the value of the Env Variable. rather it is adding to the string I pass to it.

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

I would expect a target value to be: c:\Users\UserName\dir1\dir2\dir3\filename.ext or %userprofile%\dir1\dir2\dir3\filename.ext

Instead, I am getting: C:\windows\system32%UserProfile$\dir1\dir2\dir3\filename.ext

example of output written to logfile:

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

Could anyone shed some light as to why this may be happening? The same thing is happening if i user mklink. I've added the entire script below:


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()#>
                }
            }
        }
    }

CodePudding user response:

First you have set a variable called $shortpath:

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

and then you say:

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

I would expect a target value to be: c:\Users\UserName\dir1\dir2\dir3\filename.ext or %userprofile%\dir1\dir2\dir3\filename.ext

The reason your expectation is not met is that your New-Item line doesn't refer to your $shortpath variable.

CodePudding user response:

While shortcut files (.lnk) do support cmd.exe-style environment variable references (e.g. %userprofile%) in their properties, symbolic links do not.

The target of a symbolic link must be specified as a literal path, which in your case means using PowerShell's environment-variable syntax (e.g., $env:UserProfile) in order to resolve the variable reference to its value up front:

# 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
  • Related