Search code examples
powershellshortcutcom-interoppowershell-4.0path-manipulation

Powershell: Edit the drive letter of all my shortcuts that begin with X:\


I have a situation where all the paths in the shortcut files that are located in the %AppData%\Microsoft\Windows\Start Menu\Programs folder and subfolders all point to an incorrect drive letter. This includes the Target: value, Start In: value and all the paths to the icon files as well. I'd like to change them all from X:\ to C:\

There are a couple that are correctly pointing to C:\ but there's only a handful of them.

Here is the code I was working with. I am able to change the TargetPath but not the WorkingDirectory value. I've tried removing the comment on line 20 but that creates an error about $null-valued expression. I've also tried duplicating the bit for TargetPath to WorkingDirectory but it does not change:

$folder = "C:\Temp\Shortcuts" 
[string]$from = "X:\" 
[string]$to = "C:\" 
$list = Get-ChildItem -Path $folder -Recurse  | Where-Object { $_.Attributes -ne "Directory"} | select -ExpandProperty FullName 
$obj = New-Object -ComObject WScript.Shell 

ForEach($lnk in $list) 
{ 
    $obj = New-Object -ComObject WScript.Shell 
    $link = $obj.CreateShortcut($lnk) 
    [string]$path = $link.TargetPath  
    [string]$path = [string]$path.Replace($from.tostring(),$to.ToString()) 
#   [string]$path = $link.WorkingDirectory
#   [string]$path = [string]$path.Replace($from.tostring(),$to.ToString()) 

    #If you need workingdirectory change please uncomment the below line. 
    #$link.WorkingDirectory = [string]$WorkingDirectory.Replace($from.tostring(),$to.ToString()) 
    $link.TargetPath = [string]$path 
    $link.Save() 
}

Solution

  • Line 20 in the code you posted is the trailing }, but I'm assuming this...

    #$link.WorkingDirectory = [string]$WorkingDirectory.Replace($from.tostring(),$to.ToString()) 
    

    ...is the real line 20. The reason for that error is because you are trying to call .Replace() on $WorkingDirectory instead of $link.WorkingDirectory. $WorkingDirectory, if it's not set anywhere, will evaluate to $null.

    With that corrected, PowerShell provides its own string replacement operators: -replace and -ireplace are case-insensitive, -creplace is case-sensitive. The first operand to all of these operators is a regular expression, and since a backslash in regex denotes a special character, you will need to escape the \ in your search pattern like this...

    [string]$from = "X:\\"
    

    You can then change the drive letter of the WorkingDirectory property with...

    $link.WorkingDirectory = [string] $link.WorkingDirectory -replace $from.tostring(),$to.ToString()
    

    ...or...

    $link.WorkingDirectory = [string] $link.WorkingDirectory -ireplace $from.tostring(),$to.ToString()
    

    Note that $link.WorkingDirectory, $from, and $to are already of type [String], so the [String] cast and calls to .ToString() are unnecessary and can be removed...

    $link.WorkingDirectory = $link.WorkingDirectory -replace $from,$to
    

    One tiny optimization you might make is to add an anchor to your search pattern so it won't bother to search for the drive letter beyond the absolute beginning of the [String]...

    [string]$from = "^X:\\"
    

    Also, since you are using PowerShell 3+, instead of filtering out directories like this...

    $list = Get-ChildItem -Path $folder -Recurse  | Where-Object { $_.Attributes -ne "Directory"}
    

    ...you can filter in files like this...

    $list = Get-ChildItem -Path $folder -Recurse -File
    

    Better yet, you can use the -Filter parameter to only include files with an .lnk extension, too...

    $list = Get-ChildItem -Path $folder -Recurse -File -Filter '*.lnk'