My Dotfiles Setup: A Simple and Powerful Way to Manage Your Shell
Over many years of working on the command line, I’ve tried numerous ways to manage my shell environment. I’ve used frameworks like bash-it, and I’ve tried keeping everything in a single .bash_profile. Each approach had its own set of trade-offs. Frameworks were often too bloated for my taste, and a single file became unwieldy.
This article describes the simple, modular, and powerful system I’ve developed to manage my shell configuration. It’s a system that has evolved to meet my needs, and I hope it can be a useful starting point for you to build your own perfect shell environment.
My requirements are simple:
- Easy to read and understand
- Easy to set up on any Linux environment
- A straightforward method for adding or modifying configurations
- Primary shell is
bash, because it’s ubiquitous.
Why Do It This Way?
The core idea is to break down shell customizations into self-contained files. Instead of having one massive .bash_profile, I have a directory of small, focused configuration files for different tools like git, k8s, java, etc.
This approach has several advantages:
- Simplicity and Organization: It’s much easier to find and edit a specific setting when your configurations are organized by topic.
- Modularity: You can easily enable or disable a set of configurations by creating or removing a symbolic link. This makes it trivial to tailor your shell for different machines or projects.
- Performance: By breaking up the configuration, you can profile the load time of each file and optimize your shell’s startup speed. The
timeloadfunction in mybash_profileis a testament to this. I’ve found that for some tools, pre-rendering bash completion code into a file is faster than sourcing it from a command-line tool on every shell startup. - Portability: Your entire shell setup is in a single Git repository, making it easy to clone and set up on a new machine.
Quick Start
Here’s how you can get up and running with this system. I recommend forking my repository so you can customize it to your heart’s content.
Clone your forked repository
We’ll clone the repository into a hidden directory in your home folder called
.shell.d.cd $HOME git clone git@github.com:<your-username>/shell.d.git .shell.dBack up your existing
.bash_profileIt’s always a good idea to make a backup of your existing configuration.
mv .bash_profile .bash_profile.BAKActivate your new configuration
Create a symbolic link from the repository’s
bash_profileto your home directory. This makes.bash_profilea gateway to your new modular setup.ln -s .shell.d/bash_profile .bash_profileAdd local customizations
The
local.bashfile is for machine-specific settings. For example, you might set environment variables with secrets that you don’t want to check into Git. The.shell.d/bash_profilewill automatically source this file if it exists.edit .shell.d/local.bashStart with the bare minimum here to bootstrap the rest of your customizations.
Customizing Your Environment
Now for the fun part: tailoring the shell to your needs.
The .shell.d/available directory contains all the possible configurations you can enable. To enable a configuration, you create a symbolic link to it in the .shell.d/enabled directory. The bash_profile will source all the files in the enabled directory.
For example, to enable the Kubernetes (k8s) configuration:
cd $HOME/.shell.d
ln -s available/k8s.bash enabled/k8s.bash
To disable it, you would simply remove the symbolic link:
rm .shell.d/enabled/k8s.bash
This makes it incredibly easy to turn configurations on and off without having to edit any files.
Extending Your Environment
The true power of this system comes from how easy it is to add your own customizations. If you find a tool or workflow that isn’t covered in the available directory, you can create a new file with your desired configuration.
When creating your own files, keep these principles in mind:
- Keep it modular: Each file should be self-contained and focus on a single tool or concept. For example, a
java.bashfile should contain only Java-related aliases and functions. - Keep it simple: Write your shell code to be as clear and understandable as possible. This will make it easier to maintain in the future.
- Keep it fast: Your shell’s startup time is precious. Use the
timeloadfunction in thebash_profileto measure how long your new file takes to load and optimize it if necessary.
Once you’ve created your new file in the available directory, you can enable it by creating a symbolic link, just as you did in the “Customizing Your Environment” section. For example, to add a new set of aliases for Python, you would create a new file, add your aliases, and then enable it:
# 1. Create the new file
touch .shell.d/available/python.bash
# 2. Add your aliases and functions to the file...
# 3. Enable it by creating a symlink
cd $HOME/.shell.d
ln -s available/python.bash enabled/python.bash
That’s all it takes to extend your shell with new functionality.
Conclusion
This modular approach to managing a shell environment has served me well. It’s simple, organized, and makes it easy to manage configurations across multiple machines. By keeping configurations in self-contained files, you can easily add, remove, and profile them.
I encourage you to take this setup and make it your own. Fork the repository, add your own configurations, and build the shell environment that works for you. Happy scripting!