So you wanna be a coder…
But yourcat
don’t fly
You gottaalias
things up
To get thatshell
done 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"end
By 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.
!(htop > top)[htop.png]
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 +10M
find . -type f -size +1M -size -5M
find . -type f -mtime -7
find . -type f ! -newermt 2024-01-01
fd exmaple.txt
fd -e md -t f docs
fd report --exclude backup
fd -t d config
fd --size +10M
fd --size 1M..5M
fd --changed-within 7d
fd --changed-before 2024-01-01
Of 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'end
The -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'end
zoxide > 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 endend
which 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 endend
fnm > 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 ;)