So you wanna be a coderâŠ
But yourcatdonât fly
You gottaaliasthings up
To get thatshelldone right
Daaaaamn rightâŠ
This post is a tribute to a popular post by Remy Sharp, CLI: improved.
The de-facto standard unix commands such as ls, grep, top etc. are fine, theyâve been serving us well for a few decades. Even though those are the commands that are used daily by many, they are not the most user-friendly.
As someone who lives in terminal, I wanted my daily routine to be a little better, nicer, and overall more enjoyable. Bit by bit I tweaked the CLI experience to my liking by replacing some of the old-school commands with their modern alternatives.
TL;DR
- fish >
zsh>bash - bat >
cat - htop >
top - fd >
find - ripgrep >
grep - eza >
ls - zoxide >
cd - fnm >
nvm - fzf
- starship
- tldr
- jq
- gron
- monoid
- tomorrow night
How it works?
For instance, cat prints a file on your terminal. In many instances I use it to print files containing programming code, or some structured data such as JSON, Markdown, or XML. It would be quite helpful if cat main.go would do syntax hilighting, but cat doesnât. What we can do is we can âreplaceâ cat with a CLI utility that also prints files AND does syntax highlighting. I bet there must be a handful of such programms, but the most popular is bat, which is what I use. All that needs to be done is to install bat and make cat and alias to bat.
alias cat="bat"so that when cat <file> is executed on the terminal, itâs bat that is executed instead of cat.
Drawbacks
Sometimes itâs not desireable to substitute the original command with the âimprovedâ alternative. In such a case the original command can be executed in a couple of ways.
command cat <filename>is the better option since itâs explicit and works across bash, zsh, and fish shells. This is my go-to.
Another option is to provide the full path to the original command, i.e.
/bin/cat <filename>but itâs not as convenient because one must know where the original command resides on their system. And as such this is the least favourable method.
There is another way that only works in bash/zsh which is to add a backslash anywhere in the command like so:
\cat <filename>this wouldâve been my favourite, but as I said itâs bash/zsh only, and this is definitely not that killer feature that would make me switch from fish.
Having that said, there is another method that is available for me as fish user, which is to execute cat via bash using something like bax since cat is a fish alias, which bash has no idea about:
bax cat <filename> # something fishy this way comes ;-)What I use
fish > zsh > bash
I used both bash and zsh for many years, but I switched to fish a few years ago and Iâm not looking back. Itâs fast, itâs user-friendly, and itâs pretty. I like the syntax and I have a bunch of scripts that I use daily that are written in fish.
There are some minor drawbacks, the main one is itâs not POSIX-compliant, has a little bit of learning curve, and itâs not as popular as bash or zsh, so itâs not always supported by default in some tools. But itâs popular enough to be used in those tools that I use, such as fzf, pythonâs virtualenv, starship etc.
bat > cat
I use bat like this
# upgrade catif command -sq bat alias cat="bat -pp" alias caat="bat -n" alias caaat="bat"else alias caat="cat -n" alias caaat="cat -n"endBy default I want it act like cat, but only give me syntax highlighting, no pager, no git integration, no line numbers. If I want line numbers, I use caat, and if I want the full bat experience, I use caaat.
htop > top
Everyone know about htop at this point, sometimes itâs one of the first things to be installed on a fresh production server. It provides very nice view of CPU and Memory at a glance, itâs pretty much all I need in most cases.
fd > find
fd is like find, but requires less flags, and overall nicer interface. Just comapre:
find . -name "example.txt"find docs -type f -name "*.md"find . -name "*report*" -not -path "./backup/*"find . -type d -name "config"find . -type f -size +10Mfind . -type f -size +1M -size -5Mfind . -type f -mtime -7find . -type f ! -newermt 2024-01-01fd exmaple.txtfd -e md -t f docsfd report --exclude backupfd -t d configfd --size +10Mfd --size 1M..5Mfd --changed-within 7dfd --changed-before 2024-01-01Of course, the downside is if you already mastered your craft with find, or you have a buch of scripts wrapping find, using fd might be completely unnecessary, but I find even a simple case of finding files by name is much less mouthful comapred to find. I mean I just fd file, but I could as well had an alias for find like fnd = find . -type f , but I also used to find files by size, and the fd interface is just better.
ripgrep > grep
Similar to fd, ripgrep is like grep, but the interface is nicer, and the output too.
Just like find, I still use grep, but in most cases all I need grep for is to rg DatabaseRepositoryInterface.
I tuned the default rg up like this though:
if command -sq rg alias rg='rg --no-heading -M 150'endThe -M flag truncates the output to 150 columns, otherwise if I have large generated files (such as minimised JS or CSS) the output blows up my terminal.
eza > ls
eza is just modern ls, let the comparison screenshot speak for itself:
This is my eza configuration:
# upgrade lsif command -sq eza alias ls='eza --time-style long-iso' alias l='ls -la --icons' alias la='ls -la' alias lsd='ls -D' alias lnew='l -snew' alias lnewr='l -snew -r' alias lanew='la -snew' alias lanewr='la -snew -r' alias tree='eza -T --icons'else alias l='ls -lA' alias la='ls -lA' alias lsd='ls -d *' alias lnew='l -latr' # a bit quirky, I want new to be on the bottom, because the list may be long, I don't wanna scroll alias lnewr='l -lat' alias lanew='la -latr' alias lanewr='la -lat'endzoxide > cd
zoxide keeps track of the directories you visit, and then you can jump to directories providing partial names.
Letâs say you have a bunch of projects in ~/projects and you want to jump to ~/projects/my-awesome-project, you can just type z my and it will take you there.
I use zoxide in conjunction with my p fish script which you can find here. The idea is quite simple, the script looks into predefined project directories and finds all the directories with a .git in them, and then I can jump to any of those directories by typing p <partial-project-name>. My own blog is located inside ~/Projects/coffeeaddict.dev/ on my machine, so I just type p coffee and Iâm there. What happens behind the scenes is this:
if command -sq zoxide && test -n "$project" set zoxide_match for PROJECTS_DIR in $PROJECTS_DIRS set search_term (string split "" $project) set zoxide_match $zoxide_match (string collect (zoxide query "$PROJECTS_DIR" $search_term 2> /dev/null)) end if test -n "$zoxide_match" if test (count $zoxide_match) -gt 1 z (echo $zoxide_match | string split " " | fzf) else z $zoxide_match end return endendwhich finds a zoxide match in any of the project directores, and then jumps to it.
If no match is found it lists all the projects using fzf and as soon as I select one I let zoxide to navigate to it.
if command -sq fzf set -l source set list for PROJECTS_DIR in $PROJECTS_DIRS set list $list (string collect (find $PROJECTS_DIR -maxdepth 3 -type d)) end for dir in $list if test -d "$dir/.git" set -a source "$dir" end end z (echo $source | string split " " | fzf)else
I donât replace cd as I replace ls or cat. Sometimes I donât want magic, sometimes I donât want zoxide to index the directory Iâm navigating to, so I want both cd and z commands available, and if zoxide isnât installed on my system, I fallback to cd:
# zoxide fallbackif not command -sq zoxide function z -w cd echo "zoxide is not installed! falling back to cd" cd $argv end function zi -w cd echo "zoxide is not installed! falling back to cd" cd $argv end function zoxide -w cd echo "zoxide is not installed! falling back to cd" cd $argv end function za -w cd echo "zoxide is not installed! falling back to cd" cd $argv end function zq -w cd echo "zoxide is not installed! falling back to cd" cd $argv end function zr -w cd echo "zoxide is not installed! falling back to cd" cd $argv endendfnm > nvm
fnm is just a MUCH faster nvm drop-in replacement. I hate having nvm.sh loading every time I create new shell session, which I do a lot. It irks me every time to experience the delay. fnm is just a binary, it doesnât need to be hooked up into ~/.bashrc or ~/.zshrc or ~/.config/fish/config.fish or whatever. Itâs just there, and itâs fast.
fzf
I LOVE fzf. It deserves a separate article. As soon as I found it it became indespensable for me. I use it for everything. I use it both inside shell and in vim. In a nutshell, itâs a fuzzy finder over lists of anything.
starship
Best description of what starship is is on their website:
Starship is the minimal, blazing fast, and extremely customizable prompt for any shell! Shows the information you need, while staying sleek and minimal.
I use it in fish, and itâs just perfect. Itâs fast, itâs pretty, itâs customizable.
You might already seen what my prompt looks like from this very article, so I wonât post a screenshot here xD
tldr (not really) > man
tldr isnât really a man replacement, but itâs a nice addition. It provides a quick overview of a command, and itâs community-driven, so itâs not as detailed as man, but itâs much more readable.
jq
jq is like sed + awk for JSON. By now, everyone knows about jq, so letâs move on onto gron which is less known.
gron
jq is powerful, but together with gron they are are ultimate combo!
monoid
I tried, I really tried many fonts (one of my favs are IBM Plex Mono, Jetbrains Mono, Hack, Iosevka), but I always come back to monoid. Itâs funky, itâs clumsy, itâs not perfect. Itâs charming. And itâs perfect for coding.
tomorrow night
Same with my font of choice, I tried many themes. I like gruvbox, I like nord, I like snazzy, I like oceanicnext, but tomorrow night feels like home. And guess what? I ported it to my blog ;)