GitBashshell 无法创建符号链接

当我尝试从 Git Bash shell 创建一个符号链接时,它总是失败:

ln -s /c/Users/bzisad0/Work testlink

产出:

ln: creating symbolic link `testlink' to `/c/Users/bzisad0/Work': Permission denied

除了提供错误消息之外,它所做的唯一一件事情就是创建一个名为(在本例中) testlink的空目录。

我认为 ln可执行文件没有任何问题。例如,它属于我,并标记为可执行文件:

which ln
ls -hal /bin/ln

产出:

/bin/ln


-rwxr-xr-x    1 BZISAD0  Administ      71k Sep  5 11:55 /bin/ln

我还拥有工作目录(~,也就是 /c/Users/bzisad0) :

ls -dhal .

产出:

drwxr-xr-x  115 BZISAD0  Administ      40k Sep  5 12:23 .

我有管理权限,并且我尝试用“ Run as Administrator”打开 Git Bash shell,但是没有什么区别。

我试过打开 ln.exe的 Windows 属性,并将特权级别设置为“以管理员身份运行此程序”,但这没有帮助。

我已经进入了 Windows 中的 保安高级属性,并让自己(而不是管理员组)成为所有者,但这也不能修复任何问题。

我不知所措。我不知道这个错误消息最终是来自 ln、来自 Bash 还是来自 Windows,或者我怎么可能没有这个权限。我要怎么查个水落石出?

76042 次浏览

I believe that the ln that shipped with MSysGit simply tries to copy its arguments, rather than fiddle with links. This is because links only work (sort of) on NTFS filesystems, and the MSYS team didn't want to reimplement ln.

See, for example, http://mingw.5.n7.nabble.com/symbolic-link-to-My-Documents-in-MSYS-td28492.html

A workaround is to run mklink from Bash. This also allows you to create either a symbolic link or a junction point.

Take care to send the mklink command as a single argument to cmd...

cmd /c "mklink link target"

Here are the options for mklink...

cmd /c mklink

Output:

   Creates a symbolic link.


MKLINK [[/D] | [/H] | [/J]] Link Target


/D      Creates a directory symbolic link.  Default is a file
symbolic link.
/H      Creates a hard link instead of a symbolic link.
/J      Creates a Directory Junction.
Link    specifies the new symbolic link name.
Target  specifies the path (relative or absolute) that the new link
refers to.

If you want to create links via a GUI instead ... I recommend Link Shell Extension that is a Windows Explorer plugin for creating symbolic links, hard links, junction points, and volume mount points. I've been using it for years!

Link Shell Extension

Symbolic links can be a life saver if you have a smaller SSD drive on your system C: drive and need to symbolic link some bloated folders that don't need to be on SSD, but off onto other drives. I use the free WinDirStat to find the disk space hogs.

It is possible, albeit extremely awkward, to create a symbolic link in MSysGit.

First, we need to make sure we are on Windows. Here's an example function to check that:

windows() { [[ -n "$WINDIR" ]]; }

Now, we can't do cmd /C, because MSysGit will fornicate with this argument and turn it into C:. Also, don't be tempted to use /K; it only works if you don't have a K: drive.

So while it will replace this value on program arguments, it won't on heredocs. We can use this to our advantage:

if windows; then
cmd <<< "mklink /D \"${link%/}\" \"${target%/}\"" > /dev/null
else
ln -s "$target" "$link"
fi

Also: note that I included /D because I'm interested in directory symlinks only; Windows has that distinction. With plenty of effort, you could write a ln() { ... } function that wraps the Windows API and serves as a complete drop-in solution, but that's... left as an exercise for the reader.


As a thank-you for the accepted answer, here's a more comprehensive function.

# We still need this.
windows() { [[ -n "$WINDIR" ]]; }


# Cross-platform symlink function. With one parameter, it will check
# whether the parameter is a symlink. With two parameters, it will create
# a symlink to a file or directory, with syntax: link $linkname $target
link() {
if [[ -z "$2" ]]; then
# Link-checking mode.
if windows; then
fsutil reparsepoint query "$1" > /dev/null
else
[[ -h "$1" ]]
fi
else
# Link-creation mode.
if windows; then
# Windows needs to be told if it's a directory or not. Infer that.
# Also: note that we convert `/` to `\`. In this case it's necessary.
if [[ -d "$2" ]]; then
cmd <<< "mklink /D \"$1\" \"${2//\//\\}\"" > /dev/null
else
cmd <<< "mklink \"$1\" \"${2//\//\\}\"" > /dev/null
fi
else
# You know what? I think ln's parameters are backwards.
ln -s "$2" "$1"
fi
fi
}

Also note a few things:

  1. I just wrote this and briefly tested it on Windows 7 and Ubuntu, give it a try first if you're from 2015 and using Windows 9.
  2. NTFS has reparse points and junction points. I chose reparse points, because it's more of an actual symbolic link and works for files or directories, but junction points would have the benefit of being an usable solution in Windows XP, except it's just for directories.
  3. Some filesystems, the FAT ones in particular, do not support symbolic links. Modern Windows versions do not support booting from them anymore, but Windows and Linux can mount them.

Bonus function: remove a link.

# Remove a link, cross-platform.
rmlink() {
if windows; then
# Again, Windows needs to be told if it's a file or directory.
if [[ -d "$1" ]]; then
rmdir "$1";
else
rm "$1"
fi
else
rm "$1"
fi
}

Since this is one of the top links that come up when searching for creating symbolic links in MSYS or Git Bash, I found the answer was to add set MSYS=winsymlinks:native when calling git-cmd.exe (I run ConEmu) or uncomment the same line in the msys2_shell.bat file.

For my setup, that is Git for Windows 2.11.0 installed on Windows 8.1, export MSYS=winsymlinks:nativestrict does the trick as explained on Symlink support #156.

It was important to launch the Git Bash shell as administrator as on Windows only administrators could create the symbolic links.

So, in order to make tar -xf work and create the required symbolic links:

  1. Run Git Bash shell as an administrator
  2. Run export MSYS=winsymlinks:nativestrict
  3. Run tar

I prefer PowerShell to CMD, and thought I'd share the PowerShell version of this.

In my case it consists of making symbolic links linking ~/.$file to ~/dotfiles/$file, for dotfile configurations. I put this inside a .sh script and ran it with Git Bash:

powershell New-Item -ItemType SymbolicLink\
-Path \$Home/.$file\
-Target \$Home/dotfiles/$file

Extending Camilo Martin's answer as you need to use the /j parameter switch for Windows 10; otherwise the call will just return "You do not have sufficient privilege to perform this operation."

This works for Git Bash 2.20.1.windows.1/MINGW64 (Windows 10) without administrator rights (if you can read/write both /old/path and /link/path:

original_folder=$(cygpath -w "/old/path")
create_link_new_folder=$(cygpath -w "/link/path")
cmd <<< "mklink /j \"${create_link_new_folder}\" \"${original_folder}\"" > /dev/null

For anyone who's interested in how to accomplish this in Windows 10 Git Bash 2.28.0.0.1:

You have to prefix the ln -s command with the MSYS=.. instead of executing export MSYS=.. first, namely it's just one command:

MSYS=winsymlinks:nativestrict ln -s <TARGET> <NEW_LINK_NAME>

Do

Grant yourself privileges to create symbolic links.

  1. Search for local security policies
  2. Local Policies/User Rights Assignment/Create symbolic links
  3. Take a moment to scold Windows. "Bad OS! Bad!"
  4. Profit

This grants you the privilege to create symbolic links. Note, this takes effect on the next login.

The next step is to figure out how ln is configured:

env | grep MSYS

We are looking for MSYS=winsymlink: which controls how ln creates symbolic links.

If the variable doesn't exist, create it. Note, this will overwrite the existing MSYS environment variable.

setx MSYS winsymlinks:nativestrict

Do not

Run your shell as an administrator just to create symbolic links.

Explanation

The error is somewhat self-explanatory, yet elusive.

You lack the appropriate privileges to run the command.

Why?

Be default, Windows only grants symlink creation rights to Administrators.

Cygwin has to do a song and dance to get around Windows subpar treatment of symbolic links.

Why?

Something, something "security"

¯\_(ツ)_/¯

Edit:

I just realized OP had admin rights. I leave this answer up, hoping it's useful to others.

Instead of symbolic links on Windows, I found it easier to write a small Bash script that I place in my ~/bin directory.

To start Notepad++ with the npp command, I have this file:

~/bin/npp

#!/usr/bin/bash


'/c/Program Files (x86)/Notepad++/notepad++.exe' $@

And I get the path syntax right by dragging and dropping the file from Windows Explorer into Vim.

The Windows command mklink /J Link Target doesn't seem to work any more.

git bash honors the symbolic links created by cygwin. The caveat is that the symbolic link not use, e.g., '/cygdrive/c/directory' and instead use '/c/directory'.