Bash Shell Tweaks & Tips

UPDATED AUGUST 2020

Bash is the most common Unix shell. Bash is highly ubiquitous due to it being the default user shell for various flavours of Unix including: Linux, macOS (prior to Catalina) and the Windows Subsystem for Linux (WSL).

Due to this ubiquity, and the need to maintain backward compatibility, it is rare that newer features of Bash are enabled by default. Some mistake this lack of default change for stagnation and simply move across to a different shell such as Zsh or Fish for their productivity enhancements.

This post will document a number of simple tweaks and tips, gleamed from the interwebs, that will improve user productivity when using Bash. A special note should be given to the Sensible Bash project for providing a couple noteworthy tips.

Many tweaks n’ tips noted in this post are baked into my bashrc and inputrc files.

macOS, upgrade to Bash 5.x

macOS by default ships with a very old version of Bash, version 3.2, due to licensing reasons. That version is over a decade old with many missing features.

Please upgrade to version 5.x of Bash. That is most easily accomplished by using the Homebrew package manager.

Install Homebrew followed by:

brew install bash sudo sh -c 'echo /usr/local/bin/bash >> /etc/shells' chsh -s /usr/local/bin/bash

Launch a new shell and enter echo $SHELL , it should list /usr/local/bin/bash .

I also recommend reading this post followed by installation of the GNU command line utilities for macOS.

Out of the box Bash only provides rudimentary filesystem based completions unlike the Zsh and Fish shells which offer command-aware completions. For example, shopt cd<TAB> should command-aware complete with the options of cdable_vars or cdspell as compared to completing with filenames or directories starting with cd (which is what standard Bash will do).

The bash-completion packages extends the Bash shell with intelligent completions for many common commands.

Most Linux distributions install and enable bash-completion these days.

Homebrew based Bash on macOS on the other hand will require bash-completion installation and invocation.

First install bash-completion version 2:

brew install bash-completion@2

Then source bash-completion in your ~/.bashrc file:

. $( brew --prefix ) /etc/profile.d/bash_completion.sh

Confirm that bash-completion is enabled by trying the shopt cd<TAB> example noted above, the aim is to have the cdable_vars and cdspell options displayed.

If you have never used command-aware completion before it will be a revelation how useful this capability is especially for commands such as git and ssh . Basically you want to hit the TAB key early and often.

Readline Configuration

The GNU Readline library is used by Bash, and certain other utilities, for line-editing and history management.

This library is configured by an .inputrc file in a user’s home directory. A list of recommended ~/.inputrc settings follows.

The TAB key cycles forward through the completion choices. Press an arrow key, such as right-arrow, to choose a selection. TAB: menu-complete

The Shift-TAB key cycles backward through the completion choices. This is useful if you pressed TAB too many times and overshot the desired choice. Like TAB, press an arrow key, such as right-arrow, to choose a selection. " \e [Z" : menu-complete-backward

The first press of the completion key, TAB, will display a list of choices that match the given prefix, whilst the next press of the completion key will start cycling through the available choices. set menu-complete-display-prefix on The above listed completion settings will result in Zsh AUTO_MENU -like completion behaviour.

Display completion matches upon the first press of the TAB key. set show-all-if-ambiguous on

Enable colors when completing filenames and directories. set colored-stats on Filename and directory colors are defined in the LS_COLORS environment variable.

When a completion matches multiple items highlight the common matching prefix in color. set colored-completion-prefix on The prefix color used will be the so option defined in the LS_COLORS environment variable. Note, this is a new option only available with the latest versions of Readline (version 7.0 and above) and Bash (version 4.4 and above).

Ignore case when completing. set completion-ignore-case on

Treat hypens and underscores as equivalent when completing. set completion-map-case on

Automatically append the / slash character to the end of symlinked directories when completing. set mark-symlinked-directories on

Enable incremental history navigation with the UP and DOWN arrow keys. This will use the already typed text as a required prefix when navigating through history. " \e [A" : history-search-backward " \e [B" : history-search-forward

Disable beeps and do not display control characters. set bell-style none set echo-control-characters off

Readline Shortcuts

The Readline library also provides a number of useful default shortcuts.

Go to the beginning of the line. Control-a

Go to the end of the line. Control-e

Go back one word. Alt-b

Go forward one word. Alt-f

Delete backward one word. Alt-Backspace

Delete forward one word. Alt - d

Search back through history. Control-r This does a reverse search through history, from most recent to oldest, for commands that contain the chosen text. Hitting Control-r again cycles back through the matches whilst Control-Shift-r cycles forward. Note, an improved version of reverse history search is possible when using fzf with Bash key bindings. Please refer to the fzf section below for details.

Append the last argument of the previous command to the end of the current command. Alt-. For example, after doing mkdir some-long-directory simply enter cd <Alt-.> to add in the some-long-directory component to the end of the cd command.

Bash Settings

A more productive Bash experience is achieved by enabling and using the newer features the shell has made available.

The Bash shell is configured through a .bashrc file in a user’s home directory. A list of recommended ~/.bashrc tweaks follows.

Typing a directory name just by itself will automatically change into that directory. shopt -s autocd

Automatically fix directory name typos when changing directory. shopt -s cdspell

Automatically expand directory globs and fix directory name typos whilst completing. Note, this works in conjuction with the cdspell option listed above. shopt -s direxpand dirspell

Enable the ** globstar recursive pattern in file and directory expansions. shopt -s globstar For example, ls **/*.txt will list all text files in the current directory hierarchy.

History settings. HISTCONTROL = ignoreboth:erasedups HISTIGNORE = ?:?? HISTFILESIZE = 99999 HISTSIZE = 99999 PROMPT_COMMAND = 'history -a' shopt -s histappend histverify The Readline incremental and reverse search commands are much more useful when a good amount of history is kept. Note, we will also ignore all commands that are only one or two characters long. Also, history expansions, such as !! , will not be auto-executed, instead they will expand on a new line which will provide scope for review before execution.

Display Git branch details in the prompt. if [[ -f /usr/local/etc/bash_completion.d/git-prompt.sh ]] ; then local GIT_PROMPT_PATH = "/usr/local/etc/bash_completion.d/git-prompt.sh" elif [[ -f /etc/bash_completion.d/git-prompt ]] ; then local GIT_PROMPT_PATH = "/etc/bash_completion.d/git-prompt" else local GIT_PROMPT_PATH = "/usr/share/git-core/contrib/completion/git-prompt.sh" fi GIT_PS1_SHOWDIRTYSTATE = 1 GIT_PS1_SHOWUPSTREAM = "auto" GIT_PS1_SHOWSTASHSTATE = 1 . $GIT_PROMPT_PATH PS1 = " \h\$ (__git_ps1) \w > " Note, this is only a rudimentary prompt configuration, usually you will want to spruce it up. For example, one can install and use a Bash prompt script, such as seafly or bash-git-prompt, which will emit Git details with visual flair..

Automatically shorten deep paths in the prompt. The \w option in PS1 controls whether to display the path or not. PROMPT_DIRTRIM = 4 Choose a smaller number if you wish to display less path components.

Useful Bash Aliases

A Bash alias is a user defined shortcut. Aliases save keystrokes. Do not be afraid to alias long commands with shorter aliases, they will save you time.

As a starting point, here are some of the aliases from my ~/.bashrc file.

List command. alias ls = 'ls --color --classify --human-readable' alias ll = 'ls -l' alias ll. = 'ls -la' alias lls = 'ls -la --sort=size' alias llt = 'ls -la --sort=time' We want colorization with human-readable file sizes. The last two aliases will order by size (largest to smallest) or by time (newest to oldest).

Easy directory navigation. alias -- - = 'cd -' alias .. = 'cd ..' alias ..2 = 'cd ../..' alias ..3 = 'cd ../../..' alias ..4 = 'cd ../../../..' alias ..5 = 'cd ../../../../..' No more need to type cd ../../.. , just do ..3 instead. Also, just type - to return the previous directory.

Easy persmission modification. alias 000 = 'chmod 000' alias 644 = 'chmod 644' alias 664 = 'chmod 664' alias 775 = 'chmod 775' alias 775 = 'chmod 775'

Git command. alias g = git if [[ $( uname ) = Linux ]] ; then # Assuming a modern Debian-like installation of Bash Completion. . /usr/share/bash-completion/completions/git elif [[ $( uname ) = Darwin ]] ; then # Assuming a Homebrew installation of Bash Completion. . /usr/local/etc/bash_completion.d/git-completion.bash fi complete -o default -o nospace -F _git g If you use Git, then why not save a couple characters? UPDATE (JAN 2019) : Stealing an idea from the thoughbot dotfiles. Instead of simply aliasing g to git , as noted above, make it a smart alias: alias g = '_f() { if [[ $# == 0 ]]; then git status --short --branch; else git "$@"; fi }; _f' When invoked without arguments g will do a short Git status, otherwise it will just pass on the given arguments to the git command. Status is likely to be the Git command one will execute the most, hence this simple enhancement does prove very useful in practice.

Confirm unsafe file operations. alias cp = '/bin/cp -i' alias mv = '/bin/mv -i' alias rm = '/bin/rm -i' This will prompt to confirm when copying over, moving over or deleting a file.

List all files larger than a given size. alias llfs = '_f(){ find . -type f -size "$1" -exec ls --color --classify --human-readable -l {} \; ; }; _f' For example, llfs +10k will find and display all files larger than 10 kilobytes in the currect directory hierarchy.

The z Directory Navigation Utility

There are a number of ways to navigate a file system: plain cd , pushd/popd , or defining a CDPATH environment variable as a short-circuit base directory.

The z utility provides a quicker way to change to an already visited directory. The z utility, which works in both Bash and Zsh, tracks the directories you visit with the cd command. Once trained, you can just use the z command with a portion of a path to jump to a visited directory.

The z utility is most easily installed with Homebrew:

brew install z

Install the old-fashioned way if you are not a Brew user.

Then source the z.sh file in your ~/.bashrc file:

. $( brew --prefix ) /etc/profile.d/z.sh

Usage

First you need to train z :

cd ~/dotfiles cd ~/projects/tickets_app cd ~/projects/tickets_app/src cd ~/projects/tickets cd ~/projects/tickets/src

Now you can jump using the z command with the desired path hints:

z dot # will jump to ~/dotfiles z tic # will jump to ~/projects/tickets z tic app # will jump to ~/projects/tickets_app z tic app src # will jump to ~/projects/tickets_app/src

Though not Bash specific, the z utility is a fantastic tool that all Bash users will find useful.

z by default will hook into the PROMPT_COMMAND environment variable to record cd events (the training phase). This connection may not exist when using a custom prompt script such as seafly. In that case I recommend the following settings:

_Z_NO_PROMPT_COMMAND = 1 . $( brew --prefix ) /etc/profile.d/z.sh alias c = '_f(){ cd "$@" && _z --add "$(pwd)"; }; _f'

Use the c command to record directory changes in the z database (aka training).

The fzf Utility

The fzf utility is a general-purpose line-oriented fuzzy finding command line tool.

The following posts detail some of the capabilities of fzf:

With respect to Bash and fzf, I do recommend sourcing the fzf Bash keybindings in ~/.bashrc . For example, for a Homebrew installation of fzf that would be:

. $( brew --prefix ) /opt/fzf/shell/key-binding.bash

Once sourced the following handy fzf key-bindings are available in the Bash command line:

Reverse search through Bash history using fzf as the filter. Control-r

Append fuzzy found files to the end of the current shell command. Control-t

Change to a fuzzy found sub-directory. Alt-c

The qmv Rename Utility

The qmv utility is used to rename files by way of an auto-generated document of filenames that will be opened in your editor ready for modification; the renames will be applied after the editor has exited. This utility is especially useful for bulk renames where the power of editor substitution can be used to quickly specify the desired renames.

qmv installation for macOS via Homebrew:

brew install renameutils

Linux (Debian flavoured) installation:

sudo apt install renameutils

By default, qmv will use two editor columns, in my experience this consumes too much valuable real-estate for little benefit, hence, I recommend the following Bash alias (force destination-only ):

alias qmv = 'qmv -f do'

Usage

qmv ** / * .JPG # rename to lowercase '.jpg' extension via substitution qmv * .old # rename to '.BAK' extension also via substitution

Like the z utility noted above, this qmv utility is not Bash specific, but it will also prove very useful to all Bash users.

Style

If you spend a lot of time in the command line, then I recommend styling the terminal to suit your needs.

Some suggestions:

Use a modern high-performance GPU-accelerated terminal such as Alacritty or kitty.

Use a nice monospace font. Fine choices being: Hack, Fira Code, Inconsolata and my personal favourite Iosevka.

Configure the LS_COLORS environment variable to highlight filetypes using 256 colors.

Display useful information in your prompt. Examples being: hostname, username, filesystem path and git details to name a few. Personal plug, my own Bash Seafly Prompt may be of interest

If you want to dabble into the world of Bash themes and other treats then have a look at Bash-it or Oh My Bash. Both projects claims to be to Bash what Oh My Zsh is to Zsh. Personally I recommend configuring the shell yourself, that should result in a leaner and faster shell.

Conclusion

With just a little effort the Bash shell will become a more enjoyable and productive environment.

Please enable JavaScript to view the comments powered by Disqus.