Managing my shell setup

Apologies for not being perl enough in this post .. my working environment is key to me being able to get anything done so I figure it’s a grey area I’m allowed to wander into.

Something I’ve battled with for a long time is how best to manage my bash setup and aliases.

For far too long I’d been copying a .bashrc or .bash_aliases file onto each new box I’m working on, comment out a few parts, fix a few parts, …

As I started having accounts on more machines the lack of scalability soon bacame very apparent.

Luckily (for me) I’m more than happy to steal-and-adapt ideas that other people have already thought of.

bashrc.d/

The concept I’ve stolen is the /etc/*.d/ pattern. If you’ve seen one of these directories you won’t be too shocked by the following explanation of my setup.

The gist of my setup is to have a small snippet of code and a directory of useful (turn-offable) snippets.

The Small Code Snippet

I add the following to my .bashrc if it’s missing:

if [ -d $HOME/.bashrc.d ]; then
    for x in $HOME/.bashrc.d/* ; do
        test -f "$x" || continue
        test -x "$x" || continue
        . "$x"
    done
fi

Snippet Directory

I make sure I have a directory: $HOME/.bashrc.d/

I fill it with small scripts that cover one or two features, and chmod +x the scripts that I want to have ‘turned on’.

Here’s an example directory listing:

00.home-bin
bash-completion
common
git-aliases
git-color
git-persona
git-prompt
perl6
perlbrew
perlbrew-install
README.bashrc

Some examples

I’m sure you’re all happy with the directory listing and don’t need me to spoon-feed you bash setup commands but I’m still going to!

00.home-bin

This one has a strange name because it’s one of the few cases where order matters; some later snippets call $HOME/bin scripts.

#!bash
# add my own bin to my path
if [ -d ${HOME}/bin ]; then
    PATH=${HOME}/bin:${PATH}
fi

perlbrew

In the same vein as home-bin, I also want to use my perlbrew - if I have it.

#!bash
if [ -f $HOME/perl5/perlbrew/etc/bashrc ]; then
    source $HOME/perl5/perlbrew/etc/bashrc
fi

git-aliases

I don’t spend all of my time adding paths and environment settings, I also want to minimise the amount of times I type something like

git my-custom-thingy

and see:

git: 'my-custom-thingy' is not a git command. See 'git --help'.

so I have another snippet that runs and makes sure my git aliases are all injected correctly:

#!/usr/bin/env bash
if [ `which git` -a ! -f $HOME/.bashrcd.gitalias.lock ]; then
    # we don't want to be running this multiple times concurrently
    date > $HOME/.bashrcd.gitalias.lock;

    # add aliases
    git config --global alias.st       status
    git config --global alias.ci       commit
    git config --global alias.br       branch
    git config --global alias.co       checkout
    # etc

    # and cleanup
    rm $HOME/.bashrcd.gitalias.lock;
fi

How does it help?

It probably looks like there’s still a lot of manual copy-paste required to install this on a new machine.

This is where git comes in!

Once you’re happy enough with your set-up:

cd $HOME/.bashrc.d/
git init
git add .
git commit -m 'Initial commit'

then add the remote of your choice and git push your hard work there.

Now a new machine setup is just:

git clone git://server/bashrcd $HOME/.bashrc.d

and adding the small code snippet to your .bashrc

I still have to manually copy something?!

No, of course not!

In your .bashrc.d/ folder create snippet.SOURCE. Paste the snippet into it. DO NOT make it executable!

Now you can do the following after your git clone:

cat $HOME/.bashrd.d/snippet.SOURCE >> $HOME/.bashrc
source $HOME/.bashrc

Where’s the git repo?

I said I had a remote repo. I’m sure you’re wondering where it is.

It’s on github … unfortunately it’s a private repo because I haven’t separated out the $employer specific setup. Sorry!

6 Comments

I do a similar thing with my home directory. I have a .profile.d and a .bash.d, and I have my own lib/sh and bin directories, where I have some startup files. The .profile.d directory is for systems (like Ubuntu) that don't have sh symlinked to bash, and all of the file names are appended with .sh. The .bash.d directory has only .bash files, and that way I don't feel bad using any bashisms.

Apologies for not being perl enough in this post…

None necessary. It’s a weblog site for people involved with Perl, not a Perl article site. :-)

I've been thinking about ways to consolidate my various computer setups, and this is very helpful. Thanks!

Another related idea is to provide a way for a single .bashrc (and bashrc.d) to provide some custom settings amidst the same across every system. I like the idea of everything in a single directory, and maybe using (requiring?) a digit at the beginning of the snippet file name would provide the necessary utility.

Perhaps

0-*
host specific, if [ "$snip_name" == "0-$(hostname)" ]; then source

[1-9]-*
run if executable

otherwise
warn it's not being run

Thanks for the post. I like this idea a lot. One nitpick, I think this is not necessary:


test -f "$x" || continue

Shouldn't the test -x suffice?

Leave a comment

About Chisel

user-pic I blog about Perl.