Lumachina
Lumachina

Reputation: 71

Replace shortcut with relative symlink in nested folders

I need help to convert all the Windows shortcut to relative soft links (symlinks) with PowerShell. All the original files (targets of the shortcuts) are on a single folder, and inside it there is another folder with files, shortcuts and other folders (that can contain other files, shortcuts and folders).

What I'm looking is thus make this command for every shortcut (with eventually more ../):

cmd /c 'mklink "path/shortcut.txt" "../file.txt"' && del "path/shortcut.txt.lnk"

With this code I can get the absolute path of the target of the shortcut:

dir * -Include *.lnk -Recurse | ForEach-Object {
  $sh = New-Object -ComObject WScript.Shell
  $fullpath = $sh.CreateShortcut($_.FullName).TargetPath
}

Any help? How can I make it find the path and the number of ../ needed?

Upvotes: 5

Views: 1591

Answers (3)

dan
dan

Reputation: 844

The answers already here haven't worked in newer versions of Windows 10. Revised the script and worked better for me (still didn't work on all shortcuts - i.e., the directory recursion doesn't work but if you manually navigate to each directory you have shortcuts to convert it works):

# ############################################################
#  Convert Shortcuts to Symlinks Recursively
#
#  Note: Must be run as Administrator on Win10 or higher
#  - Processes the current directory and child directories
#  - leaveZombies: specify how to cleanup when done
##############################################################

# leaveZombies - set to 1 to enable, set to 0 to destroy the shortcuts
$leaveZombies = 1

# Create a WScript.Shell COM object
$obj = New-Object -ComObject WScript.Shell;

# Get all files with a .lnk extension recursively in the current directory
dir * -Include *.lnk -Recurse | ForEach-Object {

    # Extract the target path from the shortcut file using WScript.Shell
    $target = $obj.CreateShortcut($_.FullName).TargetPath #-Replace '.*\\'

    # Resolve the relative path of the current .lnk file
    $name = $(Resolve-Path -Relative $_) -Replace '^\.\\' -Replace "\.lnk$"

    # Initialize an empty string for the relative path
    $rel = '';

    # Calculate the relative path based on the number of backslashes in the target path
    for ($i=0; $i -le ([regex]::Matches($name, '\\' )).count; $i++) {
        $rel = $rel + '..\\'
    }

    # check for file or directory target
    $isLeaf = Test-Path $target -PathType Leaf
    $leafFlag = ''
    if ($isLeaf) {
        # it's a file
        $leafFlag = ''
    }
    else {
        # a directory to link to
        $leafFlag = '/D'
    }

    # Create a symbolic link using mklink with the resolved relative path and target file
    if ($rel -eq '..\\') {
        cmd /c mklink $leafFlag "$name" "$($target)"
    }
    else {
        cmd /c mklink $leafFlag "$name" "$($rel + $target)"
    }

    if ($leaveZombies) {
        # Rename the original shortcut
        Rename-Item -Path "$($name + '.lnk')" -NewName "$('_shortcut_' + $name + '.lnk')"
    }
    else {
        # Kill all shortcuts
        Remove-Item -Path "$($name + '.lnk')"
    }
}

Upvotes: 0

Lumachina
Lumachina

Reputation: 71

I think I found how to do it. This is the script:

$obj = New-Object -ComObject WScript.Shell;
dir * -Include *.lnk -Recurse | ForEach-Object {
    $file = $obj.CreateShortcut($_.FullName).TargetPath -Replace '.*\\'
    $name = $(Resolve-Path -Relative $_) -Replace '^\.\\' -Replace "\.lnk$"
    $rel = '';
    for ($i=0; $i -le ([regex]::Matches($name, '\\' )).count; $i++) {
        $rel = $rel + '..\\'
    }
    cmd /c mklink "$name" "$($rel + $file)"
    del "$($name + '.lnk')"
}

Upvotes: 0

Moses
Moses

Reputation: 9

@huh-hulk, these sort of things that perform bulk operations on files or folders, are extremely precarious when run without being rigorously tested. you were supposed to test this on an isolated group of experimental/throw-away files and folders before you attempt it on your real files and folders :(

to clarify again for those who might end up indiscriminately wiping off massive amounts of their Windows Lnk shortcuts, this is what the original poster's PowerShell script is doing:

___Suggested FileName: Convert Windows Lnk Shortcuts to Symbolic Links.ps1

# Create a WScript.Shell COM object
$obj = New-Object -ComObject WScript.Shell;

# Get all files with a .lnk extension recursively in the current directory
dir * -Include *.lnk -Recurse | ForEach-Object {

    # Extract the target path from the shortcut file using WScript.Shell
    $file = $obj.CreateShortcut($_.FullName).TargetPath -Replace '.*\\'

    # Resolve the relative path of the current .lnk file
    $name = $(Resolve-Path -Relative $_) -Replace '^\.\\' -Replace "\.lnk$"

    # Initialize an empty string for the relative path
    $rel = '';

    # Calculate the relative path based on the number of backslashes in the target path
    for ($i=0; $i -le ([regex]::Matches($nomeok, '\\' )).count; $i++) {
        $rel = $rel + '..\\'
    }

    # Create a symbolic link using mklink with the resolved relative path and target file
    cmd /c mklink "$name" "$($rel + $file)"

    # Delete the original .lnk file
    del "$($name + '.lnk')"
}

Upvotes: 0

Related Questions