Small & opinionated selection of basic Bash configurations for a better command-line user experience
There’s a small set of simple Bash options that can dramatically improve the user experience by removing certain limitations typical of command-line interfaces. They enable small optimizations that might not seem like a big deal at first, but every keystroke saved adds up rapidly if you’re a heavy terminal user. After reading this article, you will be able to use Bash to move into directories using less keystrokes, enjoy a smarter tab completion, work with a command history that actually makes sense, and jump everywhere in your file system at the speed of thought. All this without relying on any convoluted hack, just plain native or quasi-native Bash options.
Every option listed in this article is packaged in a repo on GitHub and can be enabled from your Bash configuration file, namely
bash_profile. The difference between the two is clearly explained in this short post by Josh Staiger. If you’re on OS X, I recommend you to follow Josh’s advice of sourcing
bash_profile so to keep all your configuration in one place. I will assume you did this throughout the rest of the article.
A couple of caveats before jumping in: since changes to Bash configuration require you to reload your config file to become effective, you might want to save you some typing by setting up an alias that does that:
alias refresh='source ~/.bashrc'
Also, make sure you have the Bash Completion package installed and properly configured on your system, as some of the options described here will not work properly without it.
Smarter tab completion
Readline is the GNU library that provides an unified interface for advanced line editing to CLI programs like Bash. As command-line user, you use it all the time. Tab completion? Powered by Readline. Emacs-like key bindings like
C-w to delete back one word? Powered by Readline. Incremental history search? Powered by Readline.
The capabilities provided by Readline are so symbiotic to Bash that most users consider them native Bash features (this is what I meant above with quasi-native Bash options). They’re not, but we can still set them from our
bashrc by using the built-in Bash command
bind. Here are my favorites, each improving a different aspect of tab completion:
bind "set completion-ignore-case on" bind "set completion-map-case on" bind "set show-all-if-ambiguous on"
completion-ignore-case tells Readline to perform filename completion in a case-insensitive fashion. This is almost always what you want, and it comes in handy particularly on OS X, where system folders are capitalized by default: I no longer need to press
<Shift> when I want to
cd do<Tab> will be enough.
completion-map-case, filename matching during completion will treat hyphens and underscores as equivalent. Since in most keyboard layouts typing an underscore usually requires pressing
<Shift>, that’s another keystroke saved. This option requires
completion-ignore-case to be enabled.
show-all-if-ambiguous will get Readline to display all possible matches for an ambiguous pattern at the first
<Tab> press instead of at the second. This is another small UX improvement you will get used to in no time.
There are tons of other cool Readline runtime behaviors you can activate, like vi-like key bindings or some clever history-search tweaks. As your configuration grows, consider using a dedicated
inputrc, Readline’s default configuration file, instead of cluttering your
bashrc with too many
bind statements. This is mine.
Most of the options below are taken from the article Better Bash History by Tom Ryder. They enable history behaviors that make sense and ease the job when it comes to searching or parsing the archive. Each line is briefly explained by a comment, refer to the original post if you want to dig deeper.
# Append to the history file, don't overwrite it shopt -s histappend # Save multi-line commands as one command shopt -s cmdhist # Record each line as it gets issued PROMPT_COMMAND='history -a' # Huge history. Doesn't appear to slow things down, so why not? HISTSIZE=500000 HISTFILESIZE=100000 # Avoid duplicate entries HISTCONTROL="erasedups:ignoreboth" # Don't record some commands export HISTIGNORE="&:[ ]*:exit:ls:bg:fg:history" # Useful timestamp format HISTTIMEFORMAT='%F %T '
Better, faster directory navigation I
Here are three lovely options that will considerably speed up the way you navigate through the file system:
shopt -s autocd shopt -s dirspell shopt -s cdspell
autocd will spare you the hassle of typing
cd every time you need to navigate into a directory. You just need to type the name of your target: Bash will understand what you want and prepend
cd for you. This also works for the common shortcut
.. to go to the parent directory. Sadly, it doesn’t work for
- to go back to the previous working directory, but you can get around that by setting up an alias using this clever trick.
cdspell will get Bash to autocorrect minor spelling errors like transposed characters in directory names: the former during tab completion, the latter in arguments already supplied to the
Better, faster directory navigation II
cd will look in the current directory for possible targets you might want to move into. This behavior is defined by the environment variable
CDPATH, that thus looks like
CDPATH="." by default. You can add more paths to this variable by separating them with a colon. This is how my
Simply enough, I’ve added the directory where I keep all my projects (
~/repos) to the list of possible
cd targets. Now, whenever I want to jump to a particular project, I just have to type its name’s first letters at my prompt. As soon as I press
<Tab>, the project’s name I’m looking for will pop up in the suggestions and I’ll be able to jump into it right away, regardless of my current directory. No more typing long and complex paths at the prompt.
~/repos folder is the only place I want to be able to rapidly jump into wherever I am, so this slightly conservative
CDPATH definition is enough for my needs. Feel free to add more folders—just try to limit yourself to those that are actually important to avoid requiring a pager every time you tab-complete a
Let’s now look at another native option,
cdable_vars, that has a similar effect but allows for finer-grained control.
Better, faster directory navigation III
In case you were using this nifty hack to bookmark your favorite directories to be able to jump into them from everywhere—like I did until not so long ago—I have good news for you: You can stop using it right away. This ability is already baked into Bash and can be enabled through the native option
shopt -s cdable_vars
With this option set, we can then define and export variables containing paths to our most important directories and
cd into them from our prompt, thus enabling a simple, effective and hack-free bookmarking system:
# Don't use ~ to define your home here, it won't work. export dotfiles="$HOME/dotfiles" export repos="$HOME/repos" export documents="$HOME/Documents" export dropbox="$HOME/Dropbox"
cd documents will take you right into
~/Documents from wherever you are when you issue the command. If you have Bash Completion installed and configured, tab completion will also expand your
cdable_vars, besides the target folders you’ve set in
As said at the beginning, I came to rely on this setup so much that it has become my new default, so I packaged it in a repo on GitHub that’s meant to be something along the lines of Tim Pope’s sensible.vim. If you think I’ve missed something important, you can open an issue, send a pull request or let me know on Twitter.