1

I'm currently trying to cleanup my home directory by moving files like .vimrc, .bash_profile, etc. to a directory .dotfiles in my home directory.

The idea is to use symbolic links to these files afterwards: ln -s ~/.dotfiles/.vimrc ~/.
This works quiet fine, but I'd like to automate this process by writing my first bash script and I ran into some trouble.

The script currently looks something like this:

#!//bin/bash
# Make script executable with: chmod u+x brew.sh

# Ask for the administrator password upfront.
sudo -v

# Keep-alive: update existing `sudo` time stamp until the script has finished.
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

# Create '.other'-folder
echo "--> ~/.other"
if [ -d ~/.other ];
then
  echo "Directory ~/.other exists..."
else
  echo "Creating directory ~/.other..."
  mkdir ~/.other
fi
echo ""

# TRASH
echo "--> ~/.Trash"
if [ -d ~/.Trash ];
then
  echo "Directory ~/.Trash does exists. Moving it to ~/.other..."
  mv ~/.Trash ~/.other/
else
  echo "Directory ~/.Trash doesn't exists. Creating it in ~/.other..."
  mkdir ~/.other/.Trash
fi

echo "Linking ~/.other/.Trash to ~/.Trash..."
ln -s ~/.other/.Trash ~/.
echo ""

# BASH_HISTORY
echo "--> ~/.bash_history"
if [ -a ~/.bash_history ];
then
  echo "File ~/.bash_history does exists. Moving it to ~/.other..."
  mv ~/.bash_history ~/.other/
else
  echo "File ~/.bash_history doesn't exists. Creating it in ~/.other..."
  touch ~/.other/.bash_history
fi

echo "Linking ~/.other/.bash_history to ~/.bash_history..."
ln -s ~/.other/.bash_history ~/.
echo ""

# BASH_SESSIONS
echo "--> ~/.bash_sessions"
if [ -d ~/.bash_sessions ];
then
  echo "Directory ~/.bash_history does exists. Moving it to ~/.other..."
  mv ~/.bash_sessions ~/.other/
else
  echo "Directory ~/.bash_history doesn't exists. Creating it in ~/.other..."
  mkdir ~/.other/.bash_sessions
fi

echo "Linking ~/.other/.bash_sessions/ to ~/.bash_sessions/..."
ln -s ~/.other/.bash_sessions ~/.
echo ""

# .LOCAL
echo "--> ~/.local"
if [ -d ~/.local ];
then
  echo "Directory ~/.local does exists. Moving it to ~/.other..."
  mv ~/.local ~/.other/
else
  echo "Directory ~/.local doesn't exists. Creating it in ~/.other..."
  mkdir ~/.other/.local
fi

echo "Linking ~/.other/.local/ to ~/.local/..."
ln -s ~/.other/.local ~/.
echo ""

# .CONFIG
echo "--> ~/.config"
if [ -d ~/.config ];
then
  echo "Directory ~/.config does exists. Moving it to ~/.other..."
  mv ~/.config ~/.other/
else
  echo "Directory ~/.config doesn't exists. Creating it in ~/.other..."
  mkdir ~/.other/.config
fi

echo "Linking ~/.other/.config/ to ~/.config/..."
ln -s ~/.other/.config ~/.
echo ""

As you can see the code is pretty repetitive, but first things first. The code should work roughly like this. Check whether file (for example init.vim) exists in my home directory. If it does exists move it to either ~/.other (not so important files) or to ~/.dotfiles (important files). If it doesn't exist create a file (or directory) in ~/.dotfiles or ~/.other. Symlink afterwards.

So far the theory. The problem is, if the file does't exist in my home-directory yet - the script just creates a file in ~/.dotfiles/~/.other, and links the name in the home directory to it. This doesn't really work out in practice, since some files need specific permissions. For example neovim doesn't recognize some files if they are created with this script and it's not very efficient to just create the files before they are even used.

Is there a way to fix this (for example by creating a link, without creating the target file - I tried it once to link .bash_history to .other/.bash_history, the link worked fine, but bash couldn't write to a non existing file)? It would be optimal if new files would be created in the right place and I only need to specify the right place before?

PS: When the file already exists the script works fine (it just moved the file to the new location and links it).

  • You'll want to read about the touch command that will create the file if it doesn't exist, and chmod to give it the appropriate permissions – glenn jackman Jan 25 '16 at 14:04
  • @glennjackman I do know about the touch command, but how should I know about what permissions I need to set? I'd need to research for every single file. There has to be a more easy way. I thought about aliasing the script to command like cleanup and it loops over all files in the home directory and asks the user if he wants to move it to ~/.dotfiles or ~./other, but then the user had to actually do something and had to execute the code multiple times (if knew files are added to the home directory - I can't prevent the creation in the home directory). – LastSecondsToLive Jan 25 '16 at 14:12
  • 1
    Unrelated but what's up with the sudo tricks at the start? Doesn't seem like your script needs elevated privs. – Mat Jan 25 '16 at 14:13
  • @Mat You are probably right, I just think one time I got permission denied, when I tried to create a dir. I'm honestly not sure about it. I should probably remove it. Thanks! – LastSecondsToLive Jan 25 '16 at 14:14
1

A rewrite to reduce duplication

#!/bin/bash
# Make script executable with: chmod u+x brew.sh

# Ask for the administrator password upfront.
sudo -v

# Keep-alive: update existing `sudo` time stamp until the script has finished.
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

# Create '.other'-folder
echo "--> ~/.other"
mkdir -p ~/.other 2>/dev/null
echo ""

create_dir() {
    local pathname=$1 destination=$2 permissions=$3
    local dirname=$(basename "$pathname")

    echo "--> $pathname"
    if [ -L "$pathname" ] && [ "$(dirname "$(readlink "$pathname")")" = "$destination" ]; then
        echo "$pathname is already a symbolic link to $destination/$filename"
        return
    elif [ -d "$pathname" ]; then
        echo "Directory $pathname exists. Moving it to $destination..."
        mv "$pathname" $destination/
    else
        echo "Directory $pathname doesn't exist Creating it in $destination..."
        mkdir -p "$destination/$dirname"
    fi
    chmod "$permissions" "$destination/$direname"

    echo "Linking $destination/$dirname to $pathname ..."
    (
        cd "$(dirname "$pathname")"
        ln -s "$destination/$dirname" 
    )
    echo
}

create_file() {
    local pathname=$1 destination=$2 permissions=$3
    local filename=$(basename "$pathname")
    echo "--> $pathname"
    if [ -L "$pathname" ] && [ "$(dirname "$(readlink "$pathname")")" = "$destination" ]; then
        echo "$pathname is already a symbolic link to $destination/$filename"
        return
    elif [ -a "$pathname" ]; then
        echo "File $pathname exists. Moving it to $destination..."
        mv "$pathname" $destination/
    else
        echo "File $pathname doesn't exists. Creating it in $destination..."
        touch "$destination/$filename"
    fi
    chmod "$permissions" "$destination/$filename"

    echo "Linking $destination/$filename to ~/.bash_history..."
    (
        cd "$(dirname "$pathname")"
        ln -s "$destination/$filename"
    )
    echo ""
}

create_dir   ~/.Trash          ~/.other  755   # TRASH
create_file  ~/.bash_history   ~/.other  600   # BASH_HISTORY
create_file  ~/.bash_sessions  ~/.other  644   # BASH_SESSIONS
create_dir   ~/.local          ~/.other  755   # .LOCAL
create_dir   ~/.config         ~/.other  755   # .CONFIG

create_file  ~/.bashrc         ~/.dotfiles 644 # etc ...

Notes:

  • mkdir -p will create the directory if it does not exist
  • check that the directory/file is not already a symbolic link first.
  • if specific files require specific permissions, no choice but to specify it.
  • How did you find the needed permission so fast? This is honestly amazing! Thank you so much! – LastSecondsToLive Jan 25 '16 at 14:48
  • I did an ls -la and noted what perms my files have. – glenn jackman Jan 25 '16 at 17:40

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.