2

I use git to track my dotfiles across multiple machines. I wrote a pretty simple script in the repo, which backs up any outdated existing dotfiles and then creates symbolic links to each of the up-to-date dotfiles. Here is the script:

#!/bin/bash
############################
# makesymlinks.sh
# This script creates symlinks from the home directory to any desired dotfiles in ~/dotfiles
############################

########## Variables

dir=~/dotfiles              # dotfiles directory
olddir=~/dotfiles_old           # old dotfiles backup directory
files="bash_aliases bashrc vimrc"   # list of files/folders to symlink in homedir

##########

# create dotfiles_old in homedir
echo "Creating $olddir for backup of any existing dotfiles in ~"
mkdir -p $olddir

# move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks 
echo "Moving any existing dotfiles from ~ to $olddir"
for file in $files; do
    if [ -f ~/."$file" ]; then
        mv -n ~/."$file" ~/dotfiles_old/    #-n option means don't overwrite existing files in dotfiles_old
    fi

    #if e.g. ~/.vimrc exists after mv command, then this script must've been run before w/ .vimrc included
    if [ -f ~/."$file" ]; then
        echo "Symlink to $dir/$file already exists"
    else
        echo "Creating symlink to $dir/$file in ~"
        ln -s $dir/"$file" ~/."$file"
    fi
done

# source .bashrc
printf "\nTo complete the setup, please run the following command:\n\n"
printf "\tsource ~/.bashrc\n\n"

This script normally works just fine. Today though I started working on a new machine (remotely through TeamViewer if that matters), and when I ran this script for the first time, it deleted the directory it was in. I have no idea how it could've done that given the script I wrote, and it worked correctly the second time I ran it (after re-cloning the repository again). What went wrong, and how can I fix it? Was this somehow git's fault? Here's what my bash terminal looked like surrounding the bug (I've added some commentary with bash comments here for clarity):

drakeprovost@shatterdome:~/RoverCoreOS$ git clone https://github.com/DrakeProvost/dotfiles.git
Cloning into 'dotfiles'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 42 (delta 21), reused 29 (delta 11), pack-reused 0
Unpacking objects: 100% (42/42), done.
drakeprovost@shatterdome:~/RoverCoreOS$ cd dotfiles/
drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ls
bash_aliases  bashrc  makesymlinks.sh  README.md  vimrc
drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ./makesymlinks.sh 
Creating /home/drakeprovost/dotfiles_old for backup of any existing dotfiles in ~
Moving any existing dotfiles from ~ to /home/drakeprovost/dotfiles_old
Creating symlink to /home/drakeprovost/dotfiles/bash_aliases in ~
Creating symlink to /home/drakeprovost/dotfiles/bashrc in ~
Creating symlink to /home/drakeprovost/dotfiles/vimrc in ~

To complete the setup, please run the following command:

    source ~/.bashrc

drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ls
bash_aliases  bashrc  makesymlinks.sh  README.md  vimrc
drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ cd
drakeprovost@shatterdome:~$ ls -al #.bashrc, .vimrc, and .bash_aliases were all red symlinks here, meaning they pointed to non-existent files. Also note that the dotfiles directory has disappeared
total 144
drwxr-xr-x 26 drakeprovost drakeprovost  4096 Jul 19 22:40 .
drwxr-xr-x 12 root         root          4096 Sep 24  2019 ..
lrwxrwxrwx  1 drakeprovost drakeprovost    40 Jul 19 22:40 .bash_aliases -> /home/drakeprovost/dotfiles/bash_aliases
-rw-------  1 drakeprovost drakeprovost 11400 Feb 27 20:01 .bash_history
-rw-r--r--  1 drakeprovost drakeprovost   220 Sep 17  2019 .bash_logout
lrwxrwxrwx  1 drakeprovost drakeprovost    34 Jul 19 22:40 .bashrc -> /home/drakeprovost/dotfiles/bashrc
drwx------ 15 drakeprovost drakeprovost  4096 Oct 15  2019 .cache
drwxr-xr-x  5 drakeprovost drakeprovost  4096 Feb 20 18:08 catkin_ws
drwxr-xr-x  5 drakeprovost drakeprovost  4096 Feb 27 19:23 catkin_ws_PMCurdf
drwx------ 13 drakeprovost drakeprovost  4096 Feb 27 18:57 .config
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Desktop
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Documents
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Jul 19 22:40 dotfiles_old
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Downloads
-rw-r--r--  1 drakeprovost drakeprovost  8980 Sep 17  2019 examples.desktop
drwx------  2 drakeprovost drakeprovost  4096 Oct 15  2019 .gconf
drwx------  3 drakeprovost drakeprovost  4096 Oct 15  2019 .gnupg
-rw-------  1 drakeprovost drakeprovost  2052 Jul 19 22:31 .ICEauthority
drwx------  3 drakeprovost drakeprovost  4096 Oct 15  2019 .local
drwx------  5 drakeprovost drakeprovost  4096 Oct 15  2019 .mozilla
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Music
drwx------  6 drakeprovost drakeprovost  4096 Jul 19 22:31 .nx
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Pictures
-rw-r--r--  1 drakeprovost drakeprovost   807 Sep 17  2019 .profile
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Public
drwx------  2 drakeprovost drakeprovost  4096 Jul 19 22:31 .qt
drwxr-xr-x  4 drakeprovost drakeprovost  4096 Feb 27 19:58 .ros
drwxr-xr-x 11 drakeprovost drakeprovost  4096 Jul 19 22:40 RoverCoreOS
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Feb 13 13:45 .rviz
drwxr-xr-x  3 drakeprovost drakeprovost  4096 Oct 15  2019 snap
drwx------  2 drakeprovost drakeprovost  4096 Oct 15  2019 .ssh
-rw-r--r--  1 drakeprovost drakeprovost     0 Oct 15  2019 .sudo_as_admin_successful
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Templates
drwxr-xr-x  2 drakeprovost drakeprovost  4096 Oct 15  2019 Videos
-rw-------  1 drakeprovost drakeprovost   761 Oct 15  2019 .viminfo
lrwxrwxrwx  1 drakeprovost drakeprovost    33 Jul 19 22:40 .vimrc -> /home/drakeprovost/dotfiles/vimrc
drakeprovost@shatterdome:~$ source ~/.bashrc
bash: /home/drakeprovost/.bashrc: No such file or directory
drakeprovost@shatterdome:~$ git clone https://github.com/DrakeProvost/dotfiles.git
Cloning into 'dotfiles'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 42 (delta 21), reused 29 (delta 11), pack-reused 0
Unpacking objects: 100% (42/42), done.
drakeprovost@shatterdome:~$ cd dotfiles
drakeprovost@shatterdome:~/dotfiles$ ./makesymlinks.sh 
Creating /home/drakeprovost/dotfiles_old for backup of any existing dotfiles in ~
Moving any existing dotfiles from ~ to /home/drakeprovost/dotfiles_old
Creating symlink to /home/drakeprovost/dotfiles/bash_aliases in ~
Symlink to /home/drakeprovost/dotfiles/bashrc already exists
Creating symlink to /home/drakeprovost/dotfiles/vimrc in ~

To complete the setup, please run the following command:

    source ~/.bashrc

drakeprovost@shatterdome:~/dotfiles$ cd
drakeprovost@shatterdome:~$ ls #notice that dotfiles still exists this time
catkin_ws          Documents     Downloads         Pictures     snap
catkin_ws_PMCurdf  dotfiles      examples.desktop  Public       Templates
Desktop            dotfiles_old  Music             RoverCoreOS  Videos
drakeprovost@shatterdome:~$ source ~/.bashrc #this now works like you would expect
drakeprovost@shatterdome:~$ 
8
  • @alecxs I just tested it to confirm and that is incorrect. -f also matches for symbolic links to regular files. $ ln -s some_file test-link and in a script if [ -f ./test-link ]; then echo Links match; else echo Links dont match; fi (add shebang line)
    – Drake P
    Jul 20, 2020 at 5:24
  • Hmm, strange. If you create and run the script I posted in the comments, does it echo Links match for you? It does for me.
    – Drake P
    Jul 20, 2020 at 5:34
  • it seems test follow symlinks, -f works but only for valid links. when i touch some_file it give Links match
    – alecxs
    Jul 20, 2020 at 5:44
  • 1
    mv -n ~/."$file" ~/dotfiles_old/ should presumably be mv -n ~/."$file" "$olddir". That's not your problem but just tidying up and getting rid of red herrings.
    – Ed Morton
    Jul 21, 2020 at 11:59
  • 1
    I notice you ran the git clone the first time from ~/RoverCoreOS and the second time from just ~ so when you did cd and then ls after the first git clone it didn't show dotfiles because it exists under ~/RoverCoreOS/dotfiles rather than ~/dotfiles. So I think you ran git clone to create ~/RoverCoreOS/dotfiles and then ran your tool to make links to files under ~/dotfiles which doesn't exist. So there's another defensive check to add - [[ -d "$dir" ]] || { echo "$dir does not exist"; exit 1; }.
    – Ed Morton
    Jul 21, 2020 at 12:13

1 Answer 1

2

Here's the output in your question annotated:

drakeprovost@shatterdome:~/RoverCoreOS$ git clone https://github.com/DrakeProvost/dotfiles.git
Cloning into 'dotfiles'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 42 (delta 21), reused 29 (delta 11), pack-reused 0
Unpacking objects: 100% (42/42), done.

NOTE: you were in the directory ~/RoverCoreOS when you ran the above git clone so the above created the directory ~/RoverCoreOS/dotfiles, not ~/dotfiles.

drakeprovost@shatterdome:~/RoverCoreOS$ cd dotfiles/
drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ls
bash_aliases  bashrc  makesymlinks.sh  README.md  vimrc
drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ./makesymlinks.sh 
Creating /home/drakeprovost/dotfiles_old for backup of any existing dotfiles in ~
Moving any existing dotfiles from ~ to /home/drakeprovost/dotfiles_old
Creating symlink to /home/drakeprovost/dotfiles/bash_aliases in ~
Creating symlink to /home/drakeprovost/dotfiles/bashrc in ~
Creating symlink to /home/drakeprovost/dotfiles/vimrc in ~

To complete the setup, please run the following command:

    source ~/.bashrc

drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ ls
bash_aliases  bashrc  makesymlinks.sh  README.md  vimrc

All of the above happened in ~/RoverCoreOS/dotfiles.

drakeprovost@shatterdome:~/RoverCoreOS/dotfiles$ cd

You are now in the directory ~

drakeprovost@shatterdome:~$ ls -al #.bashrc, .vimrc, and .bash_aliases were all red symlinks here, meaning they pointed to non-existent files. Also note that the dotfiles directory has disappeared

~/dotfiles didn't disappear, it never existed. ~/RoverCoreOS/dotfiles existed and presumably still exists.

...
drakeprovost@shatterdome:~$ git clone https://github.com/DrakeProvost/dotfiles.git
Cloning into 'dotfiles'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 42 (delta 21), reused 29 (delta 11), pack-reused 0
Unpacking objects: 100% (42/42), done.

Now you've created the directory ~/dotfiles and from here on things work as you expect.

I'd recommend you modify your script to add some defensive checks. They can't stop you from doing the above but they can at least alert you of some issues and they would have caught the above problem (assuming you didn't have an old dotfiles directory with the expected files in your HOME dir), e.g.:

[[ -d "$dir" ]] || { ret="$?"; echo "dir \"$dir\" does not exist"; exit "$ret"; }

for file in $files; do
    [[ -s "$dir/$file" ]] || { ret="$?"; echo "file \"$dir/$file\" does not exist or is empty"; exit "$ret"; }
done

# create dotfiles_old in homedir
echo "Creating $olddir for backup of any existing dotfiles in ~"
mkdir -p "$olddir" || { ret="$?"; echo "Failed to create olddir \"$olddir\""; exit "$ret"; }

You can add other defensive checks like that as you see fit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.