I am one of those people who immediately opens a terminal window after my computer boots up. It's not out of necessity at this point, but the habit is there. I spent most of my career learning its magical incantations, and I find navigating through my daily tasks on the terminal makes for a more enjoyable day at work. That all said, it's nice to try something new once in a while.
I recently let go of the default command line interpreter, Bash, in favor of fish, which proudly markets itself as "a command line shell for the '90s." Here's why I switched.
I found that .bashrc is not enough
A little over a year ago I decided to stop abstracting away my operating systems and start to learn it from the ground up. I recently switched from Mac to Linux, which gave me a reason to look into porting my dotfiles to the new environment. It was refreshing; I found a ton of confusing shortcuts and duplication and paired it down to something that ports between operating systems well. Here are a few favorites:
## Nicer shell experience
export GREP_OPTIONS="--color=auto"; # make grep colorful
export LSCOLORS=gxfxbEaEBxxEhEhBaDaCaD; # make ls more colorful as well
export HISTSIZE=32768; # Larger bash history (allow 32³ entries; default is 500)
export HISTCONTROL=ignoredups; # Remove duplicates from history. I use `git status` a lot.
export HISTIGNORE="ls:cd:cd -:pwd:exit:date:* --help"; # Make some commands not show up in history
export LANG="en_US.UTF-8"; # Language formatting is still important
export LC_ALL="en_US.UTF-8"; # byte-wise sorting and force language for those pesky apps
export MANPAGER="less -X"; # Less is more
export GPG_TTY=$(tty); # for gpg key management
I've been on a journey to demystify the magic of my environment, and it is starting to make more sense.
Customization breaks my .bashrc
It's at this point of clarity that I got stuck again. I want some modern conventions in my terminal, especially auto-suggestions, syntax highlighting, and colorization of command outputs without too many dependencies, and I'd like that configuration to be independent of whatever command line interpreter I'm running. (iTerm2 is great on a Mac, but I don't want to track its cryptic configuration file in my dotfiles.) So what am I to do?
The first time I went after an answer, I solved it one step at a time:
- First, I customized my ~/.inputrc file to allow for forward and backward search.
- Then, I replaced cat with the prettified output of ccat and aliased cat to ccat in my .bashrc. This later eventually broke writing to stdout in some cases, so I stopped using it.
- Ultimately, I used Bash-it, which is a powerful plugin system for customizing Bash.
After peeling back the pieces one-by-one, I felt I understood a bit more about how my system ran. The hoops I felt like I had to jump through to get the modern experience I sought led me back to the standards that brought me to this point. I felt like I was a prisoner of history until I decided to throw it all away and try something totally different.
Easy defaults and customization with fish shell
Fish shell is a modern command-line interface with auto-suggestions, tab completion, and syntax highlighting (among other features). I especially like how from the first time you give it a try by typing fish, you get a beautiful experience:
In the first line, the command is blue because pwd is a valid command. On the second line, it is red because pdw is not a valid command. I hit Enter to show that it's true, but I could have quickly corrected the error and moved on with my day. In the third command, I want to change directory (cd) into my Development folder, and fish automatically asks whether I want to hop into my dotfiles, as I did the last time. When I hit Tab twice, it shows other options in the same directory, just like I'd expect.
If I want to customize my fish shell experience, I don't need to install any additional library or framework to do so. Fish ships with the fish_config command, which launches a graphical user interface (GUI) where you can choose different colors and prompt configurations.
What's more impressive is you can review all available functions, variables, bindings, and more from the GUI as well:
If you tune your configuration, all customizations are stored in the same place: ~/.config/fish. The screenshot above shows that I made a custom function that allows me to easily activate a Python environment using pyenv. Check out the tutorial on fish functions for more.
Be warned: fish is not like Bash
When trying out other shells, I found there were always a few differences I needed to tune, but they felt familiar overall. That is not the case with fish. Function definition, aliasing, and variables all work a little differently. There are helpful strategies for converting .bashrc and .bash_profile files into fish configuration, but it's more than I will cover in this article. So, while I've grown to love fish, I recommend taking the interactive tutorial for a spin to see if you like it.
Give fish a try for a beautiful default shell
I enjoy starting my day by opening up the terminal, and I enjoy it even more since switching to the fish shell. It's allowed me to shift away from worrying about what's in Bash to focusing on what code I plan to write. If you're looking to move away from tinkering with your terminal and focus more on code, give it a try. Use chsh to set it as your default shell, and let me know how it goes in the comments.