ProbableOdyssey

A Practical MacOS Terminal Setup for Data Scientists

I spend most of my time in the terminal these days, but it wasn’t always that way. I first got comfortable with the command line while working with Ubuntu during my Master’s degree. Later, during an internship at Exeter, I noticed my supervisor did nearly everything in the terminal — navigating files, mounting and unmounting USB drives, monitoring resources, managing environments, running scripts. It seemed like a good idea to pick it up, since the command line is everywhere and gives you far more control and flexibility than clicking through menus. I was slow to start — but that turned out to be an advantage. The command line grows with you, and there’s always a next step when you’re ready.

These days, whether I’m managing Python environments, installing tools, or automating tasks, I do it in the terminal — and that’s what this guide is here to help with.

However, the terminal is not without its sharp edges — it can be pretty unintuitive at first. So here’s a collection of notes I’ve collected recently about setting up a good terminal environment on MacOS so that it’s a more pleasant experience so getting the practice you need is easier

This guide is for data scientists and Python programmers using macOS 14 or later. I’m assuming you’re already comfortable with coding in VSCode, and that you’re curious about making the terminal a more productive space — no Vim rants here (this time).

Shell startup

When you open a new terminal session and initiate a new shell session such as zsh, the file ~/.zshrc automatically executes before giving you control of an interactive shell so that you can start each session with a set of user-defined defaults. All of our shell configuration will go in here.

There is also ~/.zprofile, which automatically executes for “login shells” (like when you login to the shell from another machine). We wont worry about the details much here, we’ll stay focussed on ~/.zshrc for this article

Understanding and Updating Your PATH for adding commands

The PATH environment variable tells your Mac where to look for programs when you type a command in the terminal. It’s a list of folders, separated by colons, that the system searches in order. For example, when you type python, MacOS checks each folder in PATH to find the python executable. Why does it matter?

If you install a tool (like Python, Node, or a custom script) and its command isn’t found, it’s likely because its folder isn’t in your PATH. Updating PATH tells your system where to find it.

You can inspect your path with this command:

1echo $PATH | tr ':' '\n'

To update your PATH, you usually add a line to your shell configuration file (~/.zshrc for most Mac users):

1export PATH="/your/new/path:$PATH"

This adds /your/new/path to the front of the list, so it takes priority. After editing the file restart your terminal to apply the change.

It’s good proactive to place your executable files into ~/.local/bin, so let’s add this line to ~/.zshrc:

1export PATH="$HOME/.local/bin:$PATH"

Checking executables

Not sure if you’re using the right version of a <program>?

Use which to print the path of the program that’s resolved from PATH:

which python3
# /usr/bin/python3

Use which -a to list all of the versions and the order in which they’re resolved

which -a pwd
# pwd is a shell builtin
# pwd is /bin/pwd

Keyboard shortcuts when typing (in the terminal and elsewhere)

The readline keybindings are surprisingly ubiquitous across most programs and systems. Learning these keybinds will instantly increase your speed in the terminal and elsewhere.

Here’s a quick cheat-sheet for the ones I use most frequently in the terminal:

Ctrl-a
    Move cursor to the start of the line
Ctrl-e
    Move cursor to the end of the line

Ctrl-k
    Delete text after the cursor to the end of the line.
Ctrl-u
    Delete text before the cursor to the start of the line.

Ctrl-w
    Delete word before cursor

Alt-f
    Move cursor forward by one word
Alt-b
    Move cursor backward by one word

And some other keybinds that are also useful for the terminal:

Ctrl-l
    Clear the screen

Ctrl-r
    Search your command history

Ctrl-d
    Exit the currently running command
Ctrl-c
    Cancel the currently running command

Ctrl-z
    Suspend the currently running command (run `fg` to return to it)

Package management with Brew

brew is an unofficial package manager for MacOS and Linux

Install it with

1/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Make sure that this line is present in ~/.zshrc:

1eval "$(/opt/homebrew/bin/brew shellenv)"

Restart your terminal, and you should be able to install tools

1brew install wget    # Add a package
2brew uninstall wget  # Remove a package

Unix has a wide array of powerful command line tools such as grep. MacOS comes with these tools too, but they behave differently than they do on Linux. It’s worth the effort to learn these tools when working with remote compute instances, and the best place to learn is locally

Installing the Linux versions of these tools give you predictable behavior that matches tutorials or StackOverflow answers written for Linux.

To replace these tools, install them from brew:

1brew install coreutils findutils grep

And add them to your PATH in ~/.zshrc:

1export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
2export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH"
3export PATH="/opt/homebrew/opt/grep/libexec/gnubin:$PATH"

There’s also some other core utilities that are worth updating too:

1brew install awk gnu-sed nano git less diffutils

Getting help with commands

Remembering the command syntax for different tools is hard. I constantly forget it, but thankfully there’s excellent documentation at hand!

Most commands have a --help flag that will print a quick summary of how to use the command.

There’s also the man pages for commands. If you need to view the full documentation of a command, run man <command> and it will show you the documentation in the terminal with the command less, which lets you page through a long document.

Navigating less seems a bit awkward, but there’s a few keybinds that will make this much easier:

/
    Start a serach for a particular keyword
n
    Jump to next search result
N
    Jump to previous search result

Ctrl-d
    Scroll a half-page down
Ctrl-u
    Scroll a half-page up

j
    Scroll one line down
k
    Scroll one line up

You can always display output of a command in less by using a pipe:

<command> --help | less

One of the best tools I’ve come across for quick help is tldr. You can install it with using

1brew install tldr

This command will print a short cheat sheet for most commands when you run tldr <command>.

UV For Python environment management and tool installation

UV is a much much faster alternative to pip and poetry, install it with

1curl -LsSf https://astral.sh/uv/install.sh | sh

Start a project with

1uv init <name>

Manage dependencies with

1uv add <dep>
2uv remove <dep>

Update the lockfile and install deps

1uv sync

Run commands in the virtual environment

1uv run <cmd>

Installing pre-commit and other python tools with UV

One of the best features about UV is uv tool install. Many tools out there are written in Python, but installing them with your system python environment is fragile because of the awful dependency management that has plagued Python for decades.

I used to use pipx, which would automatically create an isolated python environment for each tool so dependencies of one tool would not break another. It also managed creating the commands for me as well. Amazingly, uv also has this capability!

For example, pre-commit is a useful tool to have when working on projects with pre-commit hooks. I’ll write about this in more depth, but for now let’s just focus on installing it with uv:

1uv tool install pre-commit

Now this tool is installed on your system:

1which pre-commit
2# /home/blake/.local/bin/pre-commit

If you’d like to try it out, go to a git repo that uses pre-commit and install its hooks with

1pre-commit install

Git config and aliases

The file ~/.gitconfig contains your git configuration. Here’s a couple of highlights from mine

# A couple of shortened commands with shorter output
# * `git st`: Shorter git status
# * `git lg`: Shorter git log
# * `git forget`: Just discard all of my changes and bring me back to the last commit
[alias]
    st = "!git status -sb"
    lg = "!git log --oneline"
    forget = "!git reset --hard HEAD"

# Better output for `git diff`
# * Better diff algorithm
# * Colors blobs of moved code differently
# * Indicates when a file is renamed without changes
[diff]
    algorithm = histogram
    colorMoved = plain
    renames = true

# Always merge when pulling changes
# (A philosophical choice, but I think this preserves git history a bit better)
[pull]
    rebase = false

# Better `git push`
# * Automatically create new branches on remote when pushing new branches
# * Automaitcally set the upstream branch
[push]
    default = simple
    autoSetupRemote = true

# Make `git fetch` automatically prune branches that aren't on remote
[fetch]
    prune = true
    all = true

# Typo in your git command? Git will suggest what you probably meant
[help]
    autocorrect = prompt

Check out this article by Scott Chacon for more inspiration

Making the Terminal Comfortable (iTerm2, Fonts, Hotkeys)

ITerm2 provides a much better alternative to the default Terminal on MacOS with sane defaults out of the box

VSCode has its own in-built terminal, but it’s good to have a dedicated terminal on hand in case you need to run something quick. The only way to get good at using the terminal is to use it more often, so you might as well use a nice one!

I like to use a nice font for my terminal, my preference is JetBrains mono

To install this on MacOS:

1brew install --cask font-jetbrains-mono-nerd-font

Then in iTerm2, go to Preferences > Profiles > Text > Font and select “JetBrains Mono Nerd Font”, and switch on anti-aliasing.

You might also want to turn on Ligatures, which render sequences of symbols in a nice way (for example => displays as “⇒”)

For colourschemes, check out the collection from mbadolato. Pick a colourscheme you like (I personally recommend Gruvbox Dark Hard, Atom One Dark, or GitHub Dark). It looks like they also have instructions for using them in VSCode for a more cohesive experience

Changing your shell prompt is also not strictly necessary. I think this is a nice aesthetic change that adds some extra context and flair to the command line (such as the current python version or AWS region)

Starship offers a fantastic prompt, install it with

1brew install starship

And then add this snippet to your ~/.zshrc

1eval "$(starship init zsh)"

Personally I’m relatively new to this tool, but its default configuration tidies the prompt really well. But it also looks like there is a lot of customisation options available


By the end of these steps, we should have the following commands in our ~/.zshrc:

 1# Brew package manager
 2eval "$(/opt/homebrew/bin/brew shellenv)"
 3
 4# Starship prompt
 5eval "$(starship init zsh)"
 6
 7# Replace MacOS standard tools with Unix standard tools
 8export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
 9export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH"
10export PATH="/opt/homebrew/opt/grep/libexec/gnubin:$PATH"
11
12# Add local executable commands
13export PATH="$HOME/.local/bin:$PATH"

Reply to this post by email ↪