A fast shell setup

A fast programmer lives in their shell. It’s so powerful, there’s almost no reason to leave it. So, it’s important to have a fast setup. Here’s mine:

Fast Terminal: Xterm

Despite a bunch of new terminals in the past few years, ancient xterm remains the fastest.

It’s really, really fast with a mean 1.7ms of latency. 1.7ms is instant. Instant feels amazing. At first, it’s a bit jolting to have such a responsive experience. Your brain shuts off expecting to wait. But, xterm is already done. With time, your brain will adjust to the speed and the instant feedback becomes a rush. Xterm feels like a tool molded into your hand. At some point, even an extension of yourself. It works exactly the way you expect and want. Everything a tool should be.

The new terminals like Alactritty and gnome-terminal have much worse latency, 10-20ms. With this latency, your brain falls asleep. You don’t get the same dopamine rush when you have to wait. 10. ms. For. A. Character. To. Show. Up.

terminal.app for you mac users

xterm doesn’t ship to mac. That’s fine, mac users have terminal.app which is (probably) just as fast.

Fast Shell, uhhh….

Honestly, I don’t know. I’ve never left bash. I got used to bash scripting and never had a burning need to leave. This is one of my failings.

So, I’d like your help. Is sh or bash or zsh or ksh or fish faster to code in? If you have experience with multiple shells, please help! Just comment below with what you’ve used and which is faster.

Fast Prompt: The MVP

After fiddling with prompts, I’ve found the fastest prompt is the 2 character MVP (Minimal Viable Prompt):

$ 

$ and a space. That’s it.

You can change it (in bash) with

export PS1="$ "

The MVP makes you faster because it reduces clutter on the screen. Clutter, even if its expected, slows you down. Even the $ is clutter, but it’s needed to differentiate from no prompt (ex. when the previous command hasn’t finished).

Clutter (both visual and auditorial) distracts your brain which means less speed. This is why coding at home is faster than at work (unless your home is noisy). Coding at night is faster than during the day. By minimizing clutter, we maximize speed.

If you’re worried about forgetting which directory or git branch you’re in, don’t worry, I do too. I type “pwd” and “gs” (my alias for “git status”) when I forget. But usually I’m not changing directories or branches, I’m usually executing commands and writing pipelines in the same directory over and over. Therefore, the overhead of typing pwd or gs, is less than the speed boost from less clutter.

If you’re worried about forgetting which user or host you’re on, you can type echo $USER or echo $HOSTNAME. In my experience, this is very rare.

Free space!

The MVP also frees up valuable screen space. This especially matters when running 8 shells on a single laptop screen because I’m looking at files, running different commands, and need to see the output of all of them to nail down this darn bug.

When you have that many, seeing (for example) tyler@T480:~/code/competive-programming/leetcode 8 times gets old and just takes up too much space:

I can’t even write “echo test” on a single line! On my T480, the 8 shells are each 60+-4 characters wide. Not terrible, but easily eaten away by long directory names.

With my minimal prompt, however, I have ~58 characters. Much better than 7. For example, I can look at a file and grep for a method definition. It’s a toy example here, but cat file | grep -n “def method_name” is a common search of mine:

It works well for me, but I’ve been using bash for 15 years. If you’re still new, a lengthy prompt can keep you grounded. In the past, I’ve had my shell also print which git branch I’m in and if the directory has uncommitted changes in case I forgot. But these days, I type “gs” (an alias for “git status”) to find out.

Fast logging

Lastly, it helps to log our shell sessions for reference later. Seeing older commands can be much faster than trying to reconstruct the command from scratch. So many times I’ve wondered about some subtle command syntax (ex. foo). When I have a log, I can just see what I did before

cat .bashlog | grep foo

Bash has a built in history, but it doesn’t play well with multiple sessions. We need to log to a file.

Unlike the prompt which should be minimal, logs should be verbose. Data not logged is lost forever. You can log the data using Bash’s prompt_command:

prompt_command() {
    (echo "{\"datetime\": \"$(date -u "+%Y-%m-%dT%H:%M:%SZ")\", \"pwd\" : $(pwd | jq -R), \"command\" :$(history 1 | cut -d " " -f 4- | jq -R), \"exitCode\": $?}" >> ~/.bashlog &)
}

PROMPT_COMMAND="prompt_command"

It’s a bit of black magic, but the output is clear and simple (when formatted with jq):

{
  "datetime": "2021-03-15T15:11:22Z",
  "pwd": "/home/tyler",
  "command": "cat .bashlog | tail -n1 | pbcopy",
  "exitCode": 0
}

One key feature in the black magic is the use of & inside (). Using & makes the logging async in the background. Using () suppresses bash’s normal output about the background job completing.

This logs newline delimited json objects, which is perfect for jq. This means we can quickly use jq to query the logs, rather than slowly by hand.

For example, if I want to see all of the times I used jq in a command, I could write:

cat .bashlog | jq -s 'map(select(.command | contains("jq")))'

Or more tersely:

cat .bashlog | grep jq | jq -s

I don’t look at my logs often, but having logs and having jqable/greppable logs saves me a lot of time when I do.

Conclusion

A fast shell session doesn’t need lots of complicated widgets. If anything, it’s simple and free of clutter. We do need some complicated black magic to get logging right, but it creates a simple log that we can search through.

If you have any cool shell tips to go faster, no matter how small, just comment below.

If not and you liked this post, let me know in the comments below.