4 terminal applications with great command-line UIs

We look at a few well-designed CLI programs and how they overcome some discoverability problems.
742 readers like this.
An introduction to GNU Screen

Opensource.com

In this article, I'll look at a shortcoming of command-line interfaces—discoverability—and a few ways to overcome this problem.

I love command lines. My first command line was DOS 6.2, back in 1997. I learned the syntax for various commands and showed off how to list hidden files in a directory (attrib). I would carefully craft my commands one character at a time. When I made a mistake, I would proceed to retype the command from the beginning. One fine day someone showed me how to traverse the history using the up and down arrow keys and I was blown away.

Later when I was introduced to Linux, I was pleasantly surprised that up and down arrows retained their ability to traverse the history. I was still typing each character meticulously, but by now I knew how to touch type and I was doing exceedingly well with my 55 words per minute. Then someone showed me tab-completion and changed my life once again.

In GUI applications menus, tool tips and icons are used to advertise a feature for the user. Command lines lack that ability, but there are ways to overcome this problem. Before diving into solutions, I'll look at a couple of problematic CLI apps:

1. MySQL

First we have our beloved MySQL REPL. I often find myself typing SELECT * FROM and then press Tab out of habit. MySQL asks whether I'd like to see all 871 possibilities. I most definitely don't have 871 tables in my database. If I said yes, it shows a bunch of SQL keywords, tables, functions, and so on.

MySQL gif

2. Python

Let's look at another example, the standard Python REPL. I start typing a command and press the Tab key out of habit. Lo and behold a Tab character is inserted, which is a problem considering that a Tab character has no business in a Python source code.

Python gif

Good UX

Now let's look at well-designed CLI programs and how they overcome some discoverability problems.

Auto-completion: bpython

Bpython is a fancy replacement for the Python REPL. When I launch bpython and start typing, suggestions appear right away. I haven't triggered them via a special key combo, not even the famed Tab key.

bpython gif

When I press the Tab key out of habit, it completes the first suggestion from the list. This is a great example of bringing discoverability to CLI design.

The next aspect of bpython is the way it surfaces documentation for modules and functions. When I type in the name of a function, it presents the function signature and the doc string attached with the function. What an incredibly thoughtful design.

Context-aware completion: mycli

Mycli is a modern alternative to the default MySQL client. This tool does to MySQL what bpython does to the standard Python REPL. Mycli will auto-complete keywords, table names, columns, and functions as you type them.

The completion suggestions are context-sensitive. For example, after the SELECT * FROM, only tables from the current database are listed in the completion, rather than every possible keyword under the sun.

mycli gif

Fuzzy search and online Help: pgcli

If you're looking for a PostgreSQL version of mycli, check out pgcli. As with mycli, context-aware auto-completion is presented. The items in the menu are narrowed down using fuzzy search. Fuzzy search allows users to type sub-strings from different parts of the whole string to try and find the right match.

pgcli gif

Both pgcli and mycli implement this feature in their CLI. Documentation for slash commands are presented as part of the completion menu.

Discoverability: fish

In traditional Unix shells (Bash, zsh, etc.), there is a way to search your history. This search mode is triggered by Ctrl-R. This is an incredibly useful tool for recalling a command you ran last week that starts with, for example, ssh or docker. Once you know this feature, you'll find yourself using it often.

If this feature is so useful, why not do this search all the time? That's exactly what the fish shell does. As soon as you start typing a command, fish will start suggesting commands from history that are similar to the one you're typing. You can then press the right arrow key to accept that suggestion.

Command-line etiquette

I've reviewed innovative ways to solve the discoverability problems, but there are command-line basics everyone should implement as part of the basic REPL functionality:

  • Make sure the REPL has a history that can be recalled via the arrow keys. Make sure the history persists between sessions.
  • Provide a way to edit the command in an editor. No matter how awesome your completions are, sometimes users just need an editor to craft that perfect command to drop all the tables in production.
  • Use a pager to pipe the output. Don't make the user scroll through their terminal. Oh, and use sane defaults for your pager. (Add the option to handle color codes.)
  • Provide a way to search the history either via the Ctrl-R interface or the fish-style auto-search.

Conclusion

In part 2, I'll look at specific libraries in Python that allow you to implement these techniques. In the meantime, check out some of these well-designed command-line applications:

  • bpython or ptpython: Fancy REPL for Python with auto-completion support.
  • http-prompt: An interactive HTTP client.
  • mycli: A command-line interface for MySQL, MariaDB, and Percona with auto-completion and syntax highlighting.
  • pgcli: An alternative to psql with auto-completion and syntax-highlighting.
  • wharfee: A shell for managing Docker containers.

Learn more in Amjith Ramanujam's  PyCon US 2017 talk, Awesome Commandline Tools, May 20th in Portland, Oregon.

User profile image.
Amjith Ramanujam is a senior software engineer at Netflix. His team is responsible for keeping Netflix services running in the face of extreme adversity. In other words, his team is in charge of doing regional failover. In his spare time he writes modern CLI tools. He is the creator of pgcli and mycli. You should say hi to him on twitter.

11 Comments

I don't remember where I got it, but I have this in my home to provide history and tab-completion for python's default REPL:

$ cat ~/.pythonrc
import os, atexit, readline, rlcompleter

hfile = os.path.join(os.environ['HOME'], '.python_history')

try:
readline.read_history_file(hfile)
except IOError:
pass

readline.parse_and_bind('tab: complete')
atexit.register(readline.write_history_file, hfile)
del atexit, hfile

Just so you know, the python 3 REPL actually has a fine tab completion just like you'd expect.
And unless you're somehow bound to use python 2 there is little to no reason for anyone not to do the jump, at least for new projects.

You're right Python 3 is marginally better than Python 2 in that respect. It doesn't insert a tab character, but if you give bpython or ptpython a try you'll see how much better their completion feature really is.

In reply to by Manu (not verified)

Amjith, thank you so much for this article.

One of the things I like about NetBeans code composing window is the nice completion features it has. Of course this is particularly useful in "batteries included" environments like Python, Java or Groovy.

Though I haven't tried it yet, I really must just because it looks like such a great tool:

https://valloric.github.io/YouCompleteMe/

I would love to see you guys review dockly as a console UI tool to manage docker containers effectively for developers who love to stay in their terminal :)

https://github.com/lirantal/dockly

Similar to the `fish` shell, I've got history search with zsh + oh-my-zsh (can't remember which enables it). Up and down on a blank terminal scroll through previous commands similar to bash, whereas doing so on an incomplete command will complete it by scrolling through previous commands starting the same way. This set up also has a shared history between separate terminal instances, even in different directories, unless of course they're remote access with ssh.

You're right. It is possible to achieve the same results as fish by tweaking the config or installing a plugin for zsh. Which is fine, but the point I'm trying to make is in order to make those features readily accessible they should be shipped as default not enabled via a config file or through some extension.

In reply to by TomK

Thanks! I'd never heard of bpython, but that's very useful for starting out. The commands aren't clearly in the head yet and it's helpful to have that auto-suggestion. I had no idea there were tools like this in the command line.

Where is the part 2?

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.