My goals are to:
- Do it automatically (without adding a line to my script every time I add or change the location of a config file)
- The files in the git repo folder should not be hidden, but the symlinks in my home directory should
- Existing files of the same name should be overwritten, but not directories. For example if I have
config/conf.txt
in my dotfiles repo, it shouldn't overwrite the entire~/.config
folder, but simply add a link toconf.txt
inside~/.config
.
My current solution does these, but it could probably be done better:
DOTFILES_DIR="$HOME/dotfiles"
echo -n Creating symlinks...
shopt -s extglob nocaseglob
# Create hidden symlinks, hard link them to $HOME, rm old symlinks
for dotfile in $DOTFILES_DIR/!(readme*|${0##*/}); do
hidden="$DOTFILES_DIR/.${dotfile##*/}"
cp -asf "$dotfile" "$hidden"
cp -alf "$hidden" "$HOME"
rm -r "$hidden"
done
echo done
I think I should be able to copy the files directly to the home directory, but I had some trouble getting that to work right if a destination folder already existed.
Edit: I was able to eliminate the second for loop. Much better. There may still be a better way, though.
1 Answer 1
Instead of symlinks I would probably arrange for
$ make -C $HOME/dotfiles refresh
to perform some name mangling and rsync -av
copying,
or perhaps create simpler symlinks without a "hidden" layer.
But you've chosen your favorite approach,
so we'll go down that path.
Habitually add these suggested double quotes, please, to control globbing:
for dotfile in "$DOTFILES_DIR"/!(readme*|${0##*/}); do
(Yes, I know, the source makes it a constant with no SPACE
if $HOME
lacks whitespace. It's just a good habit to have.)
There seems to be more than one file-copying case covered by this code.
The cp; cp; rm
approach you've chosen is non-intuitive.
I would not be willing to maintain this codebase as presented.
An alternate design would use find $DIR -type f
plus a pipeline which mangles filenames and performs
any needed copying.
More # comments
on the cp
flag behavior might help.
Automated unit test(s) that demonstrate the code's behavior
would definitely help.
For example, it could show what happens when
conf.txt
was present for a while, and then
is removed from the dotfiles
repo.
-
\$\begingroup\$ Shouldn't
${0##*/}
also be double-quoted? I don't think we want word-splitting there - or does theextglob
option somehow prevent that? \$\endgroup\$Toby Speight– Toby Speight2023年06月13日 04:29:38 +00:00Commented Jun 13, 2023 at 4:29 -
1\$\begingroup\$ I think likely you're right. In the end, I trust unit tests; they reveal actual behavior. Initially I tried what I knew was dumb, just blindly quoting the whole thing. And
shellcheck
told me I was being dumb, observing that thefor
loop would iterate over just a single item. I feel that defining a$DIR
directory variable would help with clarity, and that afind
pipeline which selects or discards certain patterns would also be an improvement. Possibly defined as a brief function. Cramming everything into a one-liner doesn't do future maintainers any favors, as evidenced by our doubts. \$\endgroup\$J_H– J_H2023年06月13日 04:45:20 +00:00Commented Jun 13, 2023 at 4:45
cp
is for copying, not symlinking (look atln
) \$\endgroup\$cp -s
creates symlinks. I usecp
because the-a
option preserves the directory structure (point 3 above) \$\endgroup\$