Search code examples
powershelllnk

Changing the target of multiple shortcuts with PowerShell


I am writing a PowerShell script to search the C:\Users directory and change all lnk files that contain \server1 to \server2 but keep the rest of the link.

What I have so far will keep changing the LNK's target path to "Computer" and then disable you from being able to edit the LNK. Can anyone explain why this is doing this?

Code:

#Folder to search
$favourites_path = "C:\Users\" 

#Backup C:\Users DISABLED by Default "
# Copy-Item $favourites_path "$favourites_path`1" -Recurse

$favourites = Get-ChildItem $favourites_path -Recurse -Filter *.lnk
foreach ($favourite in $favourites) {
  $shortcut = (New-Object -ComObject 
  'WScript.Shell').CreateShortCut($favourite.FullName)
  $newpath=switch -Wildcard ($shortcut.TargetPath)
  {
    '"\\server1*"'           { $_ -replace '"\\server1"', '"\\server2"'}
  }
  $shortcut.TargetPath=$newpath
  $shortcut.Save()
}

Solution

    • Even though Explorer may show the Target: field of a shortcut (*.lnk file) enclosed in "...", accessing the equivalent field programmatically usually does not contain these enclosing double quotes.

    • -replace's 1st RHS operand is a regex (regular expression), in which \ is the escape character. Therefore, to replace a literal \, you must double it.

      • As written (leaving the double-quoting issue aside), your code would replace \\server1 with \\\server2.
    • Since you're recreating the shortcut file unconditionally, there is no need for a switch statement, given that -replace amounts to a no-op if the regex doesn't match anything.

      • Your switch statement has only 1 branch and no default branch, so that any target that doesn't start with \\server1 (leaving the double-quoting aside) is set to $null.

    The following code addresses these issues:

    #Folder to search
    $favourites_path = "C:\Users" 
    
    Get-ChildItem $favourites_path -Recurse -Filter *.lnk | ForEach-Object {
       $shortcut = (New-Object -ComObject 'WScript.Shell').CreateShortCut($favourite.FullName)
       $shortcut.Target = $shortcut.Target -replace '^\\\\server1\\', '\\server2\'
       $shortcut.Save()
    }
    
    • Note how the regex is anchored with ^ to the start of the string, ensuring that literal \\server1\ only matches there.

    • An alternative to manually doubling the \ instances is to use [regex]::Escape(), which escapes any regex metacharacters in the input string to ensure its treatment as a literal:

      $shortcut.Target = $shortcut.Target -replace ('^' + [regex]::Escape('\\server1\')), '\\server2\'
      
    • In the replacement string (2nd RHS operand), \ instances do not need escaping; there, only $ instances would need escaping, because $-prefixed tokens refer to parts of what the regex matched (see this answer of mine for more).