Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create relative symlink powershell way?

It is not that simple to create relative symbolic links in powershell 5.1. New-Item is not working as expected. Some approaches are listed below. Am I missing something?

Sample setup for all examples:

mkdir C:\Temp\foo -ErrorAction SilentlyContinue
'sample contents' > C:\Temp\foo\foo.txt
cd C:\Temp

Sample1: Does not work as expected

#new ps5 Item cmdlets (https://msdn.microsoft.com/en-us/powershell/wmf/5.0/feedback_symbolic) are not working well with relative paths
#C:\Temp\foo and C:\Temp\foo\foo.txt are returned
$fld = New-Item -ItemType SymbolicLink -Name 'bar' -Target '.\foo'
$fl = New-Item -ItemType SymbolicLink -Name 'bar.txt' -Target '.\foo\foo.txt'
$fld.Target
$fl.Target

Sample2: Does not work as expected

#Powershell community extensions
#same problem - paths are created as absolute: C:\Temp\foo C:\Temp\foo\foo.txt
$fld = New-Symlink 'c:\Temp\bar' '.\foo'
$fl = New-Symlink 'c:\Temp\bar.txt' '.\foo\foo.txt'
$fld.Target
$fl.Target

Sample3: Works as expected

#API call CreateSymbolicLink as per https://gallery.technet.microsoft.com/scriptcenter/new-symlink-60d2531e
#.\foo and .\foo\foo.txt are returned
Add-Type -MemberDefinition @'
    [DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);

    public static DirectoryInfo CreateSymbolicLinkToFolder(string lpSymlinkFileName, string lpTargetFileName) {
        bool res = CreateSymbolicLink(lpSymlinkFileName, lpTargetFileName, 1);
        if (!res) { throw new Win32Exception(Marshal.GetLastWin32Error()); }
        return (new DirectoryInfo(lpSymlinkFileName));
    }

    public static FileInfo CreateSymbolicLinkToFile(string lpSymlinkFileName, string lpTargetFileName) {
        bool res = CreateSymbolicLink(lpSymlinkFileName, lpTargetFileName, 0);
        if (!res) { throw new Win32Exception(Marshal.GetLastWin32Error()); }
        return (new FileInfo(lpSymlinkFileName));
    }
'@ -Name Win32 -NameSpace System -UsingNamespace System.ComponentModel, System.IO
[Win32]::CreateSymbolicLinkToFolder("c:\Temp\bar", ".\foo")
[Win32]::CreateSymbolicLinkToFile("c:\Temp\bar.txt", ".\foo\foo.txt")

Sample4: Works as expected

#using mklink from cmd produces correct relative paths
#.\foo and .\foo\foo.txt are returned
cmd /c mklink /d "c:\Temp\bar" ".\foo"
cmd /c mklink "c:\Temp\bar.txt" ".\foo\foo.txt"
(Get-Item "c:\Temp\bar").Target
(Get-Item "c:\Temp\bar.txt").Target

Edit: Sample3 has been updated to unicode api entry and GetLastError

like image 800
Anton Krouglov Avatar asked Feb 28 '17 16:02

Anton Krouglov


People also ask

How do you create a relative path in a symbolic link?

As the most common shells expands paths, to create a relative symlink you should quote the "source path". Also, the -s parameter means --symbolic . ln creates hard links by default.

How do I create a symbolic link in PowerShell?

Call the New-Item cmdlet to create symbolic links and pass in the item type SymbolicLink . Next, replace the Link argument with the path to the symbolic link we want to make (including the file name and its extension). Finally, replace the Target portion with the path (relative or absolute) that the new link refers to.

Can symbolic links be relative?

Symbolic link can be of two types Relative or Absolute.


1 Answers

Starting with pwsh 7.1.X sample1 works as expected:

#new ps5 Item cmdlets (https://msdn.microsoft.com/en-us/powershell/wmf/5.0/feedback_symbolic) are not working well with relative paths
#C:\Temp\foo and C:\Temp\foo\foo.txt are returned
$fld = New-Item -ItemType SymbolicLink -Name 'bar' -Target '.\foo'
$fl = New-Item -ItemType SymbolicLink -Name 'bar.txt' -Target '.\foo\foo.txt'
$fld.Target
$fl.Target
like image 98
Anton Krouglov Avatar answered Oct 24 '22 16:10

Anton Krouglov