--- title: Sensible Bash · Small & opinionated selection of basic Bash configurations for an improved command-line user experience date: 2016-03-03T14:45:30Z source: http://mrzool.cc/writing/sensible-bash/ tags: linux, programming, bash --- #### Written on December 10, 2015 by [mrzool][1] [ Tweet ][2] ### Small & opinionated selection of basic Bash configurations for an improved command-line user experience there's a small set of simple Bash options that removes certain limitations typical of command-line interfaces and can dramatically improve the user experience. These options 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][3] and can be enabled from your Bash configuration file, namely `bashrc` or `bash_profile`. The difference between the two is clearly explained in [this short post][4] by Josh Staiger. If you're on OS X, I recommend you to follow Josh's advice of sourcing `bashrc` from `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][5] 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][6] 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 set them from our `bashrc` anyway 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" The option **`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 `` when I want to `cd` into `Documents`, typing `cd do` will be enough. With **`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 ``, that's another keystroke saved. This option requires `completion-ignore-case` to be enabled. Lastly, **`show-all-of-ambiguous`** will get Readline to display all possible matches for an ambiguous pattern at the first `` 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][7] or some clever [history-search tweaks][8]. As your configuration grows, consider using a dedicated `initrc`, Readline's [default configuration file][9], instead of cluttering your `bashrc` with too many `bind` statements. This is [mine][10]. ### Better History Most of the options below are taken from the article [Better Bash History][11] 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][11] 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 The option **`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][12]. In addition, **`dirspell`** and **`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 `cd` command. ### Better, faster directory navigation II By default, `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 `CDPATH` looks: 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 ``, 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. My `~/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 `cd` command. 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][13] 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 `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" Now `cd documents` will take you right into `~/Documents` from wherever you are when you issue the command. If you have [Bash Completion][5] installed and configured, tab completion will also expand your `cdable_vars`, besides the target folders you've set in `CDPATH`. ## Conclusion 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][3] that's meant to be something along the lines of Tim Pope's [sensible.vim][14]. If you think I've missed something important, you can open an issue, send a pull request or let me know on [Twitter][15]. —[Mattia Tezzele][16] Thanks for reading so far! For comments, suggestions, questions and corrections you can always reach me on [Twitter][15]. You can also subscribe to the [RSS feed][17] to be notified of new posts. [1]: / "Homepage" [2]: https://twitter.com/intent/tweet?text=Sensible Bash&url=http://localhost:3000/writing/sensible-bash/&via=mrzool_&related=mrzool_ "Share on Twitter" [3]: https://github.com/mrzool/bash-sensible [4]: http://www.joshstaiger.org/archives/2005/07/bash_profile_vs.html [5]: http://bash-completion.alioth.debian.org/ [6]: https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html [7]: http://blog.sanctum.geek.nz/vi-mode-in-bash/ [8]: https://coderwall.com/p/oqtj8w/the-single-most-useful-thing-in-bash [9]: http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC9 [10]: https://github.com/mrzool/dotfiles/blob/master/inputrc [11]: http://blog.sanctum.geek.nz/better-bash-history/ [12]: http://askubuntu.com/questions/146031/bash-alias-alias-name-should-be-a-simple-dash-not-working [13]: http://jeroenjanssens.com/2013/08/16/quickly-navigate-your-filesystem-from-the-command-line.html [14]: https://github.com/tpope/vim-sensible [15]: http://twitter.com/mrzool_ [16]: http://mrzool.cc [17]: /feed.xml