Reputation: 71
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
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
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
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