Whilst reorganising my dotfiles I avoided superfluous dotfile managers and
the limitations of the "version control $HOME" method by instead including
first-line comments of the form <comment delimiter> ln <link name>
in files
which needed to be symlinked.
Examples:
~: head -n 1 ~/dotfiles/helix/config.toml
# ln ~/.config/helix/config.toml
~: head -n 1 ~/dotfiles/polybar/config.ini
; ln ~/.config/polybar/config.ini
~: head -n 1 ~/dotfiles/x/keyboard.conf
# ln /etc/X11/xorg.conf.d/00-keyboard.conf
The files can be scanned and symlinks can be created with some awk magic:
find ~/dotfiles -type f \
| xargs -I {} awk 'NR == 1 && $2 == "ln" {
system("ln -sf " FILENAME $3)
}' {}
If we change our awk to just print, add a substitution for ~
, ignore the
.git
directory when searching for files and package everything up in a
function we end up with:
list_symlinks () {
find $(dirname $0) \
-type f ! -path '*.git/*' \
| xargs -I {} awk 'NR == 1 && $2 == "ln" { print FILENAME " " $3 }' {} \
| sed "s|^./|$(pwd)/|;s|~|$HOME|"
}
list_symlinks
can then be piped into awk to create utility commands for
managing symlinks:
# create symlinks
list_symlinks | awk '{system("ln -sf " $1 " " $2)}'
# remove all symlinks
list_symlinks | awk '{system("rm " $2)}'
# list symlinks
list_symlinks | awk '{ print $2 " -> " $1 }' | column -t
Using readlink
we can enhance our symlink list to show broken symlinks:
echo -e "$( \
[[ $(readlink $2) = $1 ]] \
&& echo "\033[32m✓" \
|| echo "\033[31m✗" \
) $2 -> $1 \033[0m"
Example output:
✓ ~/.i3/config -> ~/dotfiles/i3/config
✓ ~/.zshrc -> ~/dotfiles/zsh/.zshrc
✗ ~/.xinitrc -> ~/dotfiles/x/.xinitrc
✗ ~/.gitconfig -> ~/dotfiles/git/.gitconfig
We can also use the power of awk to remove old links when we update our files (this sits nicely in a git post-commit hook).
git diff HEAD~ | awk '/\-\S*\s+ln/ {
if(prevLine ~ /@@ -1/) {
system("rm " $3)
}
} { prevLine=$0 }'
See my dotfiles for a full script which
includes escalating with sudo
when required.
What I like about this method is that:
- it still allows a self-contained dotfile directory (mine is
~/dotfiles
). - a separate record of symlinks isn't required because symlinks are specified in the linked files themselves.
- checking links is trivial.
- the only dependencies are commands common to most *nix distributions.
Try it and see what you think.