Consistent modern shell tooling on MacOS and Windows WSL for developers

Photo by Markus Spiske on Unsplash

I regularly code on both MacOS and Windows machines and I was always annoyed how different the default experiences are on each. I need to use the same tools and the same experience on both.

Windows "WSL" (Windows Subsystem for Linux) is a great tool for this you can use on Windows 10 and newer. The latest version lets you run a full Ubuntu instance that integrates seamlessly with the underlying windows instance.

By using WSL2 you can have a (mostly) identical developer experience jumping between MacOS and Windows.

Better tooling for Developers

Many of the terminal tools that come with unix environments are functionally similar to how they were 20 years ago. But other developer tooling has advanced quite a bit since then.

You can replace tools like ls or cat with modern equivalents that support full colour, unicode icons, git state and more. Terminal prompts can be made git aware and use colour to indicate state so you don't have to query git so often.

Keeping developer experience consistent across machines

Keeping any shell changes you make on one machine up to date on all the machines you code on is a nightmare without the right tooling.

This article also explains all the tools I use and how I keep the same terminal setup consistent on MacOS and Windows!

Let's go!

A quick look at default Vs modern terminal tools

Some examples

Old ls old ls output

New ls (Exa) - git aware with icons and colour hints new ls output

Old cat on a json file old cat output

New cat (bat) on the same file - syntax highlighting and formatting new cat output

But tooling is just half the story. I'll also show settings to change, aliases and scripts that will make the terminal more productive for you.

My Pre-written scripts to save you time

It took me around 30 hours to investigate and configure all of these tools on MacOS and Windows WSL.

If you want to easily install and configure all these tools in one command please check out my solution https://usemiller.dev/dev-shell.

This set of scripts will setup your entire dev environment on all your computers - Windows or Mac! You can customize them to suit your needs.

If you prefer to do it manually then read on!👇

Use WSL2 and Windows Terminal on Windows

To get a modern shell on windows you must install WSL. Run this in a cmd.exe window

wsl --install

You will be asked for a username and password because it's a new instance of linux. You can use the same username as you use on MacOS to make custom scripting easier for yourself later.

Next download and install the Windows Terminal from https://aka.ms/terminal. Windows Terminal provides tabs, profiles, colour, themes and everything you would expect from a modern terminal application.

Set wsl as the default shell for your user in Windows Terminal.

Full documentation for wsl is on their site.

Use ITerm2 on MacOS

iTerm2 is a really nice replacement for the MacOS terminal. You can have tabs, pane splitting, floating windows, transparency, image backgrounds and full colour.

It has a really nice autocomplete feature (ctrl-;), full search and history. You can store profiles for starting a set of terminals (BE + FE is very common!) just like tmux sessions. It also integrates with KeyChain.

Here is my setup for a typical node and react application running on the right-hand side and the left hand side is for working in.

showing iterm2 split panes

Full documentation for iterm2 is on their site.

Install it with brew: brew install --cask iterm2.

Iterm2 isn't available on WSL.

Install zsh (Windows WSL and older versions of MacOS)

On modern versions of MacOS zsh is the default shell. Zsh is bash compatible so there's no issues with changing WSL Ubuntu to use it also.

You can check which shell you're using by running echo $SHELL.

To install zsh on WSL grab it from apt and then set it as your default shell.

sudo apt install zsh && chsh -s $(which zsh)

Use Antigen for zsh management

Zsh supports plugins and themes. Most of these are open source and are stored in various git repos around GitHub. Manually updating and managing individual repositories for Zsh plugins is a pain. Antigen is a plugin and theme management tool that makes managing these repos much easier.

You can install plugins from any repository by just adding the repo name to antigen configuration. Antigen provides simple commands to update and install everything in one go.

Additionally antigen caches plugins until you explicitly reload the configuration, so booting your shell is quite fast even with lots of active plugins.

The antigen configuration keeps your home folder and zshrc very clean because it will go and clone any repos you need in one place. It can automatically check for updates for all plugins with antigen update.

Here's an example antigen configuration. You can see that third party plugins are just listed in shorthand as a github organisation/repository. When you've loaded everything you call antigen apply and it will set the state to your configuration.

# Load the oh-my-zsh's library.
antigen use oh-my-zsh

# Plugins from the default oh-my-zsh repo (robbyrussell's oh-my-zsh).
antigen bundle node
antigen bundle npm

# Load a theme from a repo.
antigen bundle sindresorhus/pure@main

# Tell Antigen that you're done.
antigen apply

At any time you can manually force the configuration to refresh with antigen refresh.

I use brew to install antigen on MacOS - brew install antigen.

On Windows I use the shell script - curl -L git.io/antigen > antigen.zsh.

Make sure to source antigen from the correct install location for each OS in your .zshrc.

Full documentation for antigen is on their github

Use bat instead of cat

bat is a command line utility that is similar to cat but with a few extra features. Use bat instead of cat for reading text files to the terminal.

Developers will get full colour syntax highlighting for code files. You get line numbers for code. Bat integrates with git and will show developers changes to files right in the terminal.

showing bat cat output for a json file

Install with brew on Mac

brew install bat

or apt on Windows WSL

sudo apt install -y bat

On Windows wsl bat is installed as batcat so if you're aliasing cat you should use batcat instead of bat.

On mac: alias cat=bat --paging=never!

On wsl: alias cat=batcat --paging=never

Full documentation for bat is on their github

Exa for ls

Exa is a replacement for ls that is well suited for developers. Exa integrates with git so you can easily see which files are ignored (I), modified (M), new (N) or untracked (?).

Exa also provides file-type icons, colour coding of file-types, symlink destinations right in the list, ability to have folders sorted to the top and much more.

showing exa output with git state

On a mac you install exa with brew and you can alias it to ls

brew install exa
alias ls='exa -l --group-directories-first --color=auto --git --icons --no-permissions --no-user'
alias ll='exa -lahF --group-directories-first --color=auto --git --icons'

On Windows WSL you must upgrade Ubuntu before installing exa via apt. Here is how to upgrade WSL Ubuntu from 20.04 LTS to 20.10.

# upgrading Ubuntu on Windows WSL

# change from lts only to normal updates for OS
sudo sed -i 's/prompt=lts/prompt=normal/g' /etc/update-manager/release-upgrades

# update all packages
sudo apt update && sudo apt upgrade

# this snapd package manager breaks the Ubuntu upgrade process on WSL so remove it first
sudo apt remove snapd

# check for an Ubuntu update
sudo do-release-upgrade

now you can use exa on WSL

sudo apt install -y exa

on Windows you can't use the git integration so the alias is slightly different.

alias ls='exa -l --group-directories-first --color=auto --icons --no-permissions --no-user'
alias ll='exa -lahF --group-directories-first --color=auto --icons'

Full documentation for exa is on their github

Pure Terminal Prompt

The pure terminal prompt is a very clean zsh prompt. Pure isn't an oh-my-zsh theme. It's a stand-alone prompt.

showing pure prompt

You can see that the prompt is 2 lines high. The typing/input line is just the > character and the prompt shows the current state one line above the actual prompt.

This configuration gives you a full line for typing commands every time. I find alternative fancy prompts like Agnoster that are all on single line take up too much typing space.

Pure is git-aware, you can see the * indicating that I have uncommitted changes here. The prompt will also indicate if you have commits to push or pull with ꜛ and ↓.

It's very fast and provides the same information as the Agnoster prompt but it's much cleaner.

Install this prompt with antigen

antigen bundle sindresorhus/pure@main

You must also remember to disable any oh-my-zsh theme if you have one set using the env var.

# disable zsh theme for pure prompt
export ZSH_THEME=""

Full documentation for Pure is on their github

Use the z plugin for zsh

This is the plugin that makes zsh awesome for me. I use it all the time.

z will learn the folders you usually work in over time and lets you skip to them without typing the full path.

e.g. if I often work in /Users/me/projects/my-project then typing z my-project will take me to that folder from anywhere.

Install z in your antigen configuration

antigen bundle z

Use ncdu to examine disk usage

ncdu is a utility that shows disk usage in your terminal in an ncurses interface. It's free, very fast and easy to use.

// mac
brew install ncdu

// windows wsl
sudo apt-get install -y ncdu

//to run use ncdu <path> e.g.

ncdu .

Use ? to examine the options. You can navigate around folders. You can change things like the sort order and the graph displayed.

Super handy tool!

ncdu ui

diff-so-fancy for git diff

Diff so fancy is a terminal based diff tool. It provides a nicer format and better contrasting to the built in diff tool.

This screenshot from the diff-so-fancy docs shows the differences.