9

I would like to create a symlink to ~/foo/ but when I write that, ~ is always resolved to the absolute home path. I've used ../s in the past and they work fine, is ~ not allowed?

The reason I'd like to do that is because ~ has a different absolute path depending on the machine and Linux/OSX, but always points to my home directory.

I'd like to include such a link in a git repo sync'd across several machines.

2
  • Maybe you should try to use the $HOME variable instead of ~.
    – Vinz
    Sep 29, 2015 at 10:19
  • 1
    That's exactly what I don't want to do, because $HOME is always going to get resolved the moment I create the link (isn't it?), and is therefore going to be machine-dependent.
    – Jonathan H
    Sep 29, 2015 at 10:21

4 Answers 4

7

The tilde character is expanded by the shell before the command is executed. It will be replaced by the value of $HOME. So the ln utility which creates the symlink will never see the tilde, only the full path. This path will be stored in the symlink.


In Linux, there is no otion to make a symlink variable. They are handled by the kernel. The kernel does not honor environment variables.


BSD allows such links. They are called variant symbolic links. You can do something like this:

$ ln -s ’${variable}’ linkname
$ ls -l linkname
lrwxrwxrwx 1 root root 6 Sep 29 12:33 linkname -> ’${variable}’

With varsym those variables can then be set:

varsym variable=value cat linkname
[...] contents of file with name value [...]
1
  • That seems to be specific to DragonFly BSD. I see there's been some push to port it to FreeBSD but it looks like it got rejected on security grounds. Jan 7, 2022 at 15:53
2

To address your question regarding symbolic links, "is ~ not allowed?".

Answer : ~ is not allowed. It'll allow you to create such a symlink but it won't resolve to the home directory when followed.

ln -s \~ d
ls -ld d
lrwxrwxrwx 1 steve steve 1 Sep 29 11:28 d -> ~
ls d
d
cd d
cd: d: No such file or directory
2
  • 1
    Alright then, I guess I'll just create one link for each repo.
    – Jonathan H
    Sep 29, 2015 at 10:33
  • Any character except Nul and / is allowed in a UNIX pathname component. The ~ character on the other side is only expanded by certain shells if the character appears on the command line. So this answer is not helpful.
    – schily
    Sep 29, 2015 at 12:00
1

Depending upon how much control you have over the environment, it's possible to configure a namespace that behaves in a way that's similar to what you want. Here's a script that does this:

#!/bin/bash

if [ "$(id -u)" -ne "0" ]; then
    FAKEROOT=$(mktemp -d --tmpdir "fakeroot.XXXXXXXXXX")
    unshare -Urm "$0" "${FAKEROOT}"
    rm -rf "${FAKEROOT}"
    exit 0
fi

FAKEROOT="$1"
mkdir -p "${FAKEROOT}/old_root"
for FILE in $(ls /); do ln -s "/${FILE}" "${FAKEROOT}/old_root/${FILE}"; done
for FILE in $(ls /); do ln -s "old_root/${FILE}" "${FAKEROOT}/${FILE}"; done
mount --bind "${FAKEROOT}" "${FAKEROOT}"
pivot_root "${FAKEROOT}" "${FAKEROOT}/old_root"
/bin/bash --init-file <(echo '. ~/.bashrc; symvar() { VAR=$(echo "$1" | sed "s/=.*//"); TGT=$(echo "$1" | sed "s/[^=]*=//"); ln -sf "${TGT}" "/\${${VAR}}"; }')

At this point you can then define a symlink variable with the BSD syntax:

symvar HOME=/home/your-username

and you can create symlinks that look like this:

ln -s '/${HOME}/Desktop' 'Desktop Folder'

If you combine this solution with the "revertuid" solution code* then you can include something like this in the default script:

symvar HOME="${HOME}"

example (replacing the last line in the script):

revertuid /bin/bash --init-file <(echo '. ~/.bashrc; symvar() { VAR=$(echo "$1" | sed "s/=.*//"); TGT=$(echo "$1" | sed "s/[^=]*=//"); ln -sf "${TGT}" "/\${${VAR}}"; }; symvar HOME="${HOME}";')
  • Note: if any of your user ids are not 1000 then you will need to tweak the revertuid example code. All you would need to do is to pass the UID to the subscript and hand that to revertuid as the first argument.
2
  • This looks like some pretty intense voodoo... the kind of thing you'd normally warn people "not to try at home" :)
    – Jonathan H
    Aug 16, 2021 at 21:25
  • A lot of the voodoo here is just how unprivileged namespaces work, the general concept is to create a namespace that shares all the same files as the normal system but that you can then add symlinks to the root path of the namespace with the variable names (${HOME}) you want to use.
    – Compholio
    Aug 18, 2021 at 18:55
0

I solved this problem by putting relative symlinks in my project that reference a symlink in the project root. In the project root I created a symlinkToHomeDir.README file that says to run the following command:

ln -s ~ ./symlinkToHomeDir

You could create that symlink as part of a project initialization script.

The root level symlink is in my .gitignore file so it doesn't get copied to other user's directories.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .