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.txtin my dotfiles repo, it shouldn't overwrite the entire~/.configfolder, but simply add a link toconf.txtinside~/.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 theextgloboption 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
shellchecktold me I was being dumb, observing that theforloop would iterate over just a single item. I feel that defining a$DIRdirectory variable would help with clarity, and that afindpipeline 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
cpis for copying, not symlinking (look atln) \$\endgroup\$cp -screates symlinks. I usecpbecause the-aoption preserves the directory structure (point 3 above) \$\endgroup\$