Git Bash Shell fails to create symbolic links

孤者浪人 提交于 2019-11-27 10:47:15

It is possible, albeit extremely awkward, to create a symlink 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.


Edit: 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 Win7 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 symlink and works for files or directories, but junction points would have the benefit of being an usable solution in XP, except it's just for directories.
  3. Some filesystems, the FAT ones in particular, do not support symlinks. 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
}
Tony O'Hagan

A workaround is to run mklink from Bash. This also allows you to create either a Symlink or a Junction.

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

cmd  /c "mklink link target"

Here's the options for mklink ...

$ cmd /c mklink
   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, Hardlinks, Junctions, and Volume Mountpoints. I've been using it for years!

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

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 here: https://github.com/git-for-windows/git/pull/156 It's 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 symlinks:

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

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

Since this is one of the top links that come up when searching for creating symlinks 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

Extending Camilo Martin's anwser 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 Admin 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

I prefer Powershell to CMD, and thought i'd share the powershell version of this.

In my case it consists of making symlinks 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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!