Foreword
Just like Gentoo Revisited, I’m still distro-hopping to find the alternative distro for Void Linux and recently occupied with NixOS. While I started on nixos-unstable, NixOS-25.11 happened to just release a few days after I setup it. Perfect timing :)
Anyway, I took the opportunity to finally do the migration from powerlevel10k (p10k) to starship as p10k is in maintenance mode to say the least. Also, last time I refreshed my p10k.zsh was 2022-09 and in my previous blog (made in bm) I published a post on 2020-04 (last updated on 2022-10) introducing how I follow the blog workflow to stay within terminal as much as possible. In dotfiles and mission-impossible I also hinted on my cli setup. As I sort of expose (read: leak) my dev workflow in Build the Ultimate Linter with Mise, an accompanied post about my current cli setup sounds quite reasonable to me so here it is.
Besides blog and dev related tools, I’ll include a few other tools for daily tasks. Also, I’ll share some tips on optimizing zsh startup time to deprecate dotfiles#Improvement. TBA as it’s too much work.
As my git extension and dev toolchain is revealed in mise-as-linter#TLDR, they are excluded on purpose in this post.
TLDR
Same
- Terminal: any
- Shell: Bash for
/bin/sh, Zsh for everything else - Multiplexer: Tmux
- Editor: Vim
- Pager: less, bat
- TBB: w3m
- SSH: mosh+rlwrap ssh -> mosh + bare ssh
- File Browser: nnn
- Calendar: calcurse
- Music: cmus -> cmus + cmusfm
Diff
- root shell: sudo + sudoedit -> sudo w/
sudo -e-> doas/OpenDoas +sudo -e-> sudo-rs1 - ls: exa -> eza, tree -> eza –tree (lt)
- fetch: screenfetch -> neofetch -> screenfetch -> fastfetch
- GNU grep -> ugrep
- Checksum: sha512sum -> b3sum
- gzip -> pigz, zstd, zip/unzip native -> cosmocc
- cp -> install/rsync
- Data: jq -> jq + jc, visidata + sqlite-utils
- Download: curl+wget -> wcurl, youtube-dl + you-get -> yt-dlp + you-get + gallery-dl
- Spellcheck: pangu -> autocorrect + vale
- cmp/xxd+diff -> xdelta3
- Podman -> LXC via incus -> QEMU/KVM via libvirt
- Email: afew + notmuch + mutt -> afew + notmuch + neomutt -> afew + notmuch + alot -> afew + notmuch + meli
Info
Same
I don’t really care about terminal as we are not SIXEL yet. Usually I just pick up whichever available. On KDE Plasma, Konsole; Windows, Windows Terminal; Xfce4, xfce4-terminal; GNOME, GNOME Terminal; Kali, qterminal. On Void I use qterminal simply because of its size. I do plan to try kitty or wezterm someday but I doubt that I’d be convinced to make a shift.
Zsh has its own section as I have a sophisticated setup. Void links /bin/sh to dash for speed and FreeBSD defaults to tcsh/sh but I simply overwrite it to bash for maximum compatibility across multiple systems. No, I don’t use fish even if it has excellent out of box experience and I think it’s quite easy to make a good non POSIX-compliant shell.
Similarly, I just use good old tmux with oh-my-tmux. Zellij sounds good but not good enough to make a shift.
I still use Vim as I still have not learnt how to use Emacs. I’ve heard of meow mode instead of evil mode though, maybe I’ll try it inside NixOS soon™.
Also, I still use less as pager. I hardly use batcat nowadays, although I don’t know why. Sometimes I would use glow to render Markdown files but it’s also quite rare.
W3M is still my favorite text-based browser, albeit poorly maintained.
I continue to use mosh for remote server and dropped rlwrap --no-warnings ssh for local connections as nowadays I tend to carry my dotfiles everywhere and rlwrap makes more trouble than the little convenience it brings.
For file browser, I still use nnn aliased to a single n with autocd on. Many good alternatives exist now, but I don’t have to the intention to migrate. On LFS I still use fff since it’s written in Bash.
I hardly use calcurse now as many events are managed via iCalendar, but it’s still there for private stuff.
I returned to Last.FM from Libre.FM since I don’t know when, because it just works™. For Libre.FM things are usually more complicated and I don’t bother to fix it. I still use cmus even though my issue regarding RIFF tag support opened a few years ago is still not replied, let alone getting a fix.
Diff
I migrated from sudo and sudo -e to doas/OpenDoas, only to discover that the persist feature is potentially unsafe on everywhere (including FreeBSD) except OpenBSD, and the lack of sudo -e is a big issue for me. When sudo-rs finally added sudo -e support in 0.2.8, I happily adopt it in the long last. On Alpine/OpenBSD I still use default doas and on other distros I also keep sudo as failsafe just in case.
exa died at some point so I switched to community maintained eza. As tree is not always installed, I have alias lt="eza --tree" as failsafe.
Neofetch archived even before screenfetch developer declared its sunsetting, and now I use fastfetch. Still, on LFS I use screenfetch to avoid Rust toolchain.
After breaking up with BusyBox grep on Alpine which does not recognize certain GNU grep parameter, I picked up ugrep because
- it’s the only 100% compatible implementation other than GNU grep itself back then (perhaps not the only one now)
- on
legacyenterprise level distro like Rocky9, the default GNU grep is outdated for years, ugrep nicely fills the gap - it has full Unicode support by default, which is used in my zsh plugin to avoid writing commands containing certain keywords and CJK characters to
HISTFILE
I prefer blake3 hash now as it’s fast, secure, and parallelizable. sha512sum is still good, but rather slow.
Similarly, pigz is just gzip in parallel. Zstd is fast (see A Random Summary of Compressed Linux Backup, I also use it in zram). Zip/Unzip built with cosmopolitan libc works almost everywhere.
Sometimes install is better than cp as you can tweak permission/owner within the same command. rsync is fast and smart enough.
jc is used to convert output to JSON, which can be piped to jq and simplify things. I tried many cli tools that can read data from CSV, XLS, SQLite and visidata is so far the best one. With sqlite-utils I can easily publish data via datasette. My game-stats website is powered by datasette, datasette-dashboards and datasette2vercel btw.
curl now offers a wcurl script to have a similar experience like wget for lazy people like me, although it’s not well-known yet. On the contrary, everyone knows youtube-dl is killed by RIAA2 under the silly DMCA. gallery-dl is another cool tool like yt-dlp but for images and supports lots of sites.
I use autocorrect and vale to lint my prose, although it’s only used in my blog and not anywhere else.
xdelta is a good tool to make binary patch, useful in ROM hacking, although I have not made game localization patch with it yet.
I ditched podman to embrace incus at some point but I mostly just boot up a VM now.
I still use notmuch plus afew to organize my emails, and moved from alot to meli because mailcap module is removed in Python 3.13. However, I forgot why I dropped mutt/neomutt. I kind of miss it today, maybe I’ll set it up again and switch back.3
Easter Egg
As my dotfiles evolves, from time to time I’ll create/find an alias/function with a funny name, sometimes due to historical reasons, mostly for fun.4 Below are some examples.
- NB, not an acronym for nota bene or 牛 逼
, but an alias for
NO_PROXY=127.0.0.1 LC_ALL=zh_CN.UTF-8 newsboat. - PC, not personal computer or 胖 次
, but an alias for
proxychains4(aka. proxychain-ng). - DK, not Donkey Kong or danshi kousei (男子校生), but an alias for
docker, which itself is an alias forpodman. Similarly, DC is not Detective Comics or danshi chugakusei (男子中学生) butdocker-compose. - Oh, and GV is not Google Voice, definitely not Gay Video, but an alias for
TZ=UTC gpg --no-options --keyid-format long --verify.
NTR, not netorare (寝取られ) but an alias for the new nix cli or NixOS to do soft garbage collection:
# nix.zsh
if command -v nix &>/dev/null; then
# nix cli
alias ntr="nix-env --delete-generations old && nix-store --gc"
# NixOS
if command -v nixos-rebuild &>/dev/null; then
alias ntr="sudo nix-env --delete-generations old && sudo nix-store --gc"
fi
fi
It derived from my apt alias atr for sudo apt autoremove and similarly:
- In Manjaro (pamac) it’s
ptrforpamac remove -o && pamac clean --keep 3 - and a function on Arch (pacman)
sudo pacman -Rs "$(pacman -Qdtq)" && paccache -r. - Of course, for Void (xbps) it’s
xtraka.sudo xbps-remove -R -oO.
Sadly g, gi are aliases for git and e is exit so I still use ptr on Gentoo (emerge/portage) for sudo emerge --depclean. Otherwise it would become a cool Nissan GT-R5 as well. Maybe I’ll introduce it once I get my hands on Guix someday.
I have many more funny examples but it seems I spend too much time on writing this (and Zsh part is even not included!) so let’s call it a day.
Now that I mention this, I have to complain about uutils-coreutils: it is SLOW, which is weird for a Rust rewrite. Compiling is much slower if I don’t want a dirty multi-call binary,
timeoutis significantly slower and it took me a few weeks to figure out that the 100ms latency in myperfectstarship config is caused by uutils… Compatibility would eventually become good enough, but performance issue really sucks. Either way, it’s still slightly better than the ancient coreutils-9.4 on Void and I’d still adopt it when it’s about time. ↩︎Recording Industry Association of America ↩︎
Update on 2025-12-19: never mind, just realized again that it does not support reading
.emlout of box. ↩︎which also explains why my scoop bucket is named sb. You’ll get it if you understand why
ncfornatcatis funny. ↩︎Speaking of Japan, I also have
ssforslackpkg search, definitely not Shadowsocks or Schutzstaffel. ↩︎