An approximation of the *NIX ln (link) symlink command.
Set-StrictMode -Off;
# bodge the ln command
$usage = "usage: ln [-s] <target> [link_name]"
$src = '
using System;
using System.Runtime.InteropServices;
public class kernel {
[DllImport("kernel32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.I1)]
public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, uint dwFlags);
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
}'
function isadmin {
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = new-object System.Security.Principal.WindowsPrincipal $id
$p.isinrole([security.principal.windowsbuiltinrole]::administrator)
}
function symlink($target, $link_name, $is_dir) {
# CreateSymbolicLink:
# http://msdn.microsoft.com/en-us/library/aa363866.aspx
$dwFlags = 0
if($is_dir) { $dwFlags = 1 }
$kernel = add-type $src -passthru
$result = $kernel::createsymboliclink($link_name, $target, $dwFlags)
if(!$result) { "failed!"; exit 1 } # mysterious
}
function hardlink($target, $link_name) {
# CreateHardLink:
# http://msdn.microsoft.com/en-us/library/aa363860.aspx
$kernel = add-type $src -passthru
$result = $kernel::createhardlink($link_name, $target, [intptr]::zero)
if(!$result) { "failed!"; exit 1 } # mysterious
}
$symbolic = $false
$target = $args[0]
$link_name = $args[1]
$is_dir = $false
if($target -like '-s') {
$symbolic = $true
$target = $args[1]
$link_name = $args[2]
}
if(!$target) { "ln: target is required"; $usage; exit 1 }
if(!$link_name) {
# create link in working dir, with same name as target
$link_name = "$pwd\$(split-path $target -leaf)"
} elseif(!([io.path]::ispathrooted($link_name))) {
$link_name = "$pwd\$link_name"
}
if(!(test-path $target)) {
"ln: $target`: No such file or directory"; exit 1
}
if(test-path $link_name) {
"ln: $link_name`: File exists"
}
$abstarget = "$(resolve-path $target)"
if([io.directory]::exists($abstarget)) {
$is_dir = $true
}
if($abstarget -eq $link_name) {
"ln: target and link_name are the same"; $usage; exit 1
}
if(!$symbolic -and $is_dir) {
"ln: Can't create hard links for directories: use -s for symbolic link"; $usage; exit 1
}
if(!(isadmin)) {
if(gcm 'sudo' -ea silent) {
"ln: Must run elevated: try using 'sudo ln ...'."
} else {
if(gcm 'scoop' -ea silent) {
"ln: Must run elevated: you can install 'sudo' by running 'scoop install sudo'."
} else { "ln: Must run elevated" }
}
exit 1
}
if($symbolic) {
symlink $target $link_name $is_dir
} else {
hardlink $target $link_name
}
exit 0