HiveBrain v1.2.0
Get Started
← Back to all entries
patternbashMinor

Shell script that copies configuration and dotfiles to repository

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scriptcopiesshellthatrepositoryandconfigurationdotfiles

Problem

A while ago I had the idea to store all the important configuration and dotfiles from my system in a Git repository. That saved me a few months ago: My harddrive died, but I could get back to work with Ubuntu and that repository quickly.

Only problem: I maintained that repo by manually copying the files from the various directories to the repo directory. That’s what I wrote the shell script for.

What does the script do?

  • Checks whether the command is executed from within the root of a git repository



  • Detects which OS it’s operating on (Windows, Linux or OS X) and saves that information in a variable OS



  • Assigns variables for certain tool groups (like Bash, Git, etc.) and creates the target directories in the repo if they don’t exist



  • Copies file by file to these destinations



Notes:

  • Currently, I separate the files on a per-OS basis. This is probably unnecessary and even a hinderance. I’d like to here your opinion here



  • The script currently does not use any Bash >4 features, because besides Ubuntu I mainly work on Windows. I use Git Bash there which only has version 3 bundled.



  • I also wrote a short article about this script: Backup configuration and dotfiles with Git and a shell script



  • If you’re interested in current/updated versions, check out the script in the repo: get-dotfiles.sh



```
#!/bin/bash

################################################################################
#
# Shell script to copy important configuration files from the current
# environment to this repository.
#
################################################################################

# Exit as soon as a command fails
set -e

# Accessing an empty variable will yield an error
set -u

# Assume the working directory is the path to the dotfiles repository
REPO_PATH="$PWD/"

# Abort mission when we’re not in a git repository
if [[ ! -d .git ]] || ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Not a git repository."
exit 1
fi

# Detect OS (OS X, Linux o

Solution

This is pretty nicely written. I have a couple of tips to improve it though.

Don't include trailing / in path variables

Although there's absolutely nothing wrong with this:

REPO_PATH="$PWD/"
# ...
OS="osx/"
# ...
BASH_DEST="${REPO_PATH}${OS}bash/"


In my experience, not including the trailing slash is typically more ergonomic, and sometimes even less error-prone:

-
You might have a function that's expecting the trailing slash, and some callers might forget to append it correctly, and there's no reasonable way to enforce at "compile time".

-
When I see concatenated path-like variables like $REPO_PATH${OS}bash I'm always suspicious if the caller added the slash or not.
It makes me constantly look over my shoulders and re-check all uses.

Consider this alternative:

REPO_PATH=$PWD
# ...
OS=osx
# ...
BASH_DEST="$REPO_PATH/$OS/bash"


Here, I know that "$REPO_PATH/$OS/bash" will be definitely close to what I expect,
and no need to worry about forgotten slashes anywhere.
I also know it's a path,
which is less obvious in the case of the original.

Don't execute the same thing repeatedly

When you detect the OS,
the script might execute uname 3 times.
It would be better to run it once and save in a variable.

Avoid expr

expr is archaic. Usually there are better solutions.
You could also simplify the OS detection conditions using [[ instead:

uname=$(uname -s)   

# Detect OS (OS X, Linux or Windows)
if [[ $uname = Darwin ]]; then
    echo "Detected system: OS X"
    OS=osx
    ST_DIR="$HOME/Library/Application Support/Sublime Text 3/Packages/User/"
elif [[ $uname == *Linux* ]]; then
    echo "Detected system: Linux"    
    OS=linux
    ST_DIR="$HOME/.config/sublime-text-3/Packages/User/"
elif [[ $uname == *MINGW32_NT* ]]; then
    echo "Detected system: Windows"   
    OS=win
    ST_DIR="$HOME/AppData/Roaming/Sublime Text 3/Packages/User/"
    NPM_DIR="$HOME/AppData/Roaming/npm/node_modules/npm/"
fi


Quote paths that may contain spaces

It's not obvious that these paths will never contain spaces:

declare -a destinations=($BASH_DEST $GIT_DEST $NPM_DEST $RUBY_DEST $ST_DEST)


It's better to be safe, and put double-quotes around each.

More careful error handling

If you couldn't detect the OS, I suggest to print a warning and exit.

Use case for switches

The final OS-specific operations could be written a bit cleaner using a case statement.

Code Snippets

REPO_PATH="$PWD/"
# ...
OS="osx/"
# ...
BASH_DEST="${REPO_PATH}${OS}bash/"
REPO_PATH=$PWD
# ...
OS=osx
# ...
BASH_DEST="$REPO_PATH/$OS/bash"
uname=$(uname -s)   

# Detect OS (OS X, Linux or Windows)
if [[ $uname = Darwin ]]; then
    echo "Detected system: OS X"
    OS=osx
    ST_DIR="$HOME/Library/Application Support/Sublime Text 3/Packages/User/"
elif [[ $uname == *Linux* ]]; then
    echo "Detected system: Linux"    
    OS=linux
    ST_DIR="$HOME/.config/sublime-text-3/Packages/User/"
elif [[ $uname == *MINGW32_NT* ]]; then
    echo "Detected system: Windows"   
    OS=win
    ST_DIR="$HOME/AppData/Roaming/Sublime Text 3/Packages/User/"
    NPM_DIR="$HOME/AppData/Roaming/npm/node_modules/npm/"
fi
declare -a destinations=($BASH_DEST $GIT_DEST $NPM_DEST $RUBY_DEST $ST_DEST)

Context

StackExchange Code Review Q#90972, answer score: 6

Revisions (0)

No revisions yet.