+++ date = "2020-04-29T05:04:26+00:00" publishdate = "2023-12-29T07:08:55+00:00" title = "Tweaking the Bash Shell" slug = "tweaking-the-bash-shell" author = "Thedro" tags = ["bash"] type = "posts" summary = "The Bourne Again Shell is ubiquitous among many unix-like systems. Here's a few tweaks that are applied to my bash configuration for improved interactivity." draft = "" syntax = "1" toc = "" updated = "2020-05-27" +++ {{< image source="/images/tweaking-the-bash-shell.png" title="Bourne-again shell" >}} Bourne-Again Shell {{< /image >}} The [Bourne Again](https://tiswww.case.edu/php/chet/bash/bashtop.html) {{< sidenote mark="Shell" set="left" >}}[`fish`](https://fishshell.com/) and [`zsh`](https://ohmyz.sh/) are popular alternative shells.{{< /sidenote>}} is ubiquitous among many unix-like systems. Here's a few tweaks that are applied to my `bash` configuration for {{< sidenote mark="improved" set="right" >}}My preference is to not add too many tweaks (like aliases) to avoid building up bad muscle memory.{{< /sidenote>}} interactivity. ## Conditional Prompt Colors The color {{< sidenote mark="distinction" set="left" >}}This acts as a guide when ascending and descending through shells.{{< /sidenote>}} in the prompt gives a visual cue for the current environment. Green is indicative of a secure shell connection (SSH), red of `root` privilege, blue of the standard user, and white (an unmodified prompt) of a foreign system. {{< image source="/images/bash-tweaks-color-prompts.gif" title="Prompt Colors in Bash" >}} Prompt color changes based on the current context {{< /image >}} This is achieved by short circuiting the `$PS1` (the primary {{< sidenote mark="prompt" set="right" >}}My preferred `PS1` is not particularly verbose.{{< /sidenote>}} variable) in `~/.bash_profile`. ```bash PS1_USER='\[\e[0;34m\]\W\[\e[0m\] \[\e[0;34m\]\$\[\e[0m\] ' PS1_ROOT='\[\e[0;31m\]\W\[\e[0m\] \[\e[0;31m\]\$\[\e[0m\] ' PS1_SSHD='\[\e[0;32m\]\W\[\e[0m\] \[\e[0;32m\]\$\[\e[0m\] ' [ "$EUID" != 0 ] && export PS1="$PS1_USER"; [ "$EUID" = 0 ] && export PS1="$PS1_ROOT"; [ -n "$SSH_CLIENT" ] && [ "$EUID" != 0 ] && export PS1="$PS1_SSHD"; ``` ## Exit Codes Often there is a need to know the return or exit status of the last command. This is extremely useful in varied situations. The variable `$?` contains the exit status of the last command. {{< image source="/images/bash-tweaks-return-codes.gif" title="Prompt Return Codes" >}} Prompt shows the exit status of the last command {{< /image >}} In `~/.bash_profile`, the logic `$(E=$? && [ "$E" != 0 ] && echo "$E ")` is prefixed to the `$PS1`. The exit status will be printed on a carriage return. ```bash PS1_USER='$(E=$? && [ "$E" != 0 ] && echo "$E ")\[\e[0;34m\]\W\[\e[0m\] \[\e[0;34m\]\$\[\e[0m\] ' PS1_ROOT='$(E=$? && [ "$E" != 0 ] && echo "$E ")\[\e[0;31m\]\W\[\e[0m\] \[\e[0;31m\]\$\[\e[0m\] ' PS1_SSHD='$(E=$? && [ "$E" != 0 ] && echo "$E ")\[\e[0;32m\]\W\[\e[0m\] \[\e[0;32m\]\$\[\e[0m\] ' [ "$EUID" != 0 ] && export PS1="$PS1_USER"; [ "$EUID" = 0 ] && export PS1="$PS1_ROOT"; [ -n "$SSH_CLIENT" ] && [ "$EUID" != 0 ] && export PS1="$PS1_SSHD"; ``` ## Fuzzy Finder The command line fuzzy finder [fzf](https://github.com/junegunn/fzf) has [keybindings](https://github.com/junegunn/fzf#key-bindings-for-command-line) for common operations in `bash` and `zsh`. This gives the {{< sidenote mark="shell" set="right" >}}The terminal emulator in use is `urxvt`. [Simple Terminal](https://st.suckless.org/) (`st`) is also rather delightful and popular among the purists.{{< /sidenote>}} a more opinionated reverse lookup (`CTRL+R`) workflow. {{< image source="/images/bash-tweaks-fzf.gif" title="Command Line Fuzzy Finder" >}} Command Line Fuzzy Finder {{< /image >}} Source the fuzzy finder's keybindings in the `~/.bashrc` configuration. This only works if the command `fzf` has been installed on the {{< sidenote mark="system." set="left" >}}These paths are derived from Debian and Arch based systems.{{< /sidenote>}} ```bash [ -f '/usr/share/fzf/completion.bash' ] && . /usr/share/fzf/completion.bash [ -f '/usr/share/fzf/key-bindings.bash' ] && . /usr/share/fzf/key-bindings.bash [ -f '/usr/share/doc/fzf/examples/completion.bash' ] && . /usr/share/doc/fzf/examples/completion.bash [ -f '/usr/share/doc/fzf/examples/key-bindings.bash' ] && . /usr/share/doc/fzf/examples/key-bindings.bash ``` ## Vi Mode Bash has a built in readline `vi` mode. This removes the tempting dependency on the mouse for precise text manipulation. The prefixed `+` and `:` {{< sidenote mark="symbols" set="right" >}}You can use anything you want.{{< /sidenote>}} indicate `insert` and `normal` modes respectively. Pressing `v` in normal mode invokes `vim` on the current command. {{< image source="/images/bash-tweaks-vi-mode.gif" title="Vi Mode" >}} Vi Mode {{< /image >}} Use the `set` command in the `~/.inputrc` {{< sidenote mark="file" set="right" >}}Create this file inside the user's home directory.{{< /sidenote>}} to enable showing the `vi` mode prompt. ```bash set show-mode-in-prompt on set vi-ins-mode-string "+ " set vi-cmd-mode-string ": " ``` Use the `set` command to enable `vi` {{< sidenote mark="mode" set="left" >}}There is also an `emacs` mode.{{< /sidenote>}} by adding the directive in `~/.bashrc`. ```bash set -o vi; ``` ## Readline Configuration The shell's `readline` can be configured in the `~/.inputrc` file. Here's a few other `readline` tweaks for a sane setup. ```bash # Set colors on completion results. set colored-stats on # Ignore case when using tab completion. set completion-ignore-case on # Show completion results on the first tab press. set show-all-if-ambiguous on # Avoid inserting tab completions in the middle of a word. set skip-completed-text on # Up and down reverse search will consider the currently typed command. "\e[A": history-search-backward "\e[B": history-search-forward ```