Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Commander: Command Line Utility for Hackers and Poets

Commander is a task-automation utility. The basic idea is:

  • You write functions to do useful things in python
  • You decorate them with the @command decorator commander provides
  • You can now trigger your functions from the command line by name
  • You don't need to write boilerplate code and wrapper scripts
  • You can directly integrate your python commander functions into your interactive shell (!)

Commander designed to run in one of several modes:

  1. As a standalone interactive REPL where you directly type commands
  2. Integrated into your existing shell. Your commander functions are available directly in your shell as if they were shell functions.
  3. As a build tool similar to Make, Rake, or Shovel

What can I do with it?

Your commander functions can do just about anything since they are written in python. Out of the box, commander provides helpers to make the following things trivial.

  • Opening one or more browser URLs (bookmarks)
  • quick web based searches
  • Opening applications
  • running programs and scripts
  • miscellaneous system automation
  • Integrate with Keyboard Maestro on OS X for automation that is challenging from python such as manipulating windows, simulating mouse clicks, and so on

Here are some ideas to seed your mind with commander functions you may want to write.

  • Tweet something
  • Search for something on google, wikipedia, youtube, or any site or search engine
  • quickly check weather, stocks, sports scores, etc from the command line
  • quickly append notes to a journal file with a timestamp
  • add an item to your to-do list
  • see the last few git commit messages in a particular repository
  • perform an advanced renaming operation on all files under the current directory
  • perform a smart search/replace across a set of files
  • whatever else you feel would be useful to do from a command prompt
  • Anything that works better in python than as a shell script


Commander works well as a git submodule under your dotfiles repository. Set it up like this.

cd ~/dotfiles
git submodule add
cp -a commander/

Then edit your script. Read the samples there to get the hang of it. Once you see how it works, feel free to delete the sample command functions defined there.

Running Commander in single-command mode

By default, commander will start up, interpret the command given on the command line, then exit. This mode is good for:

  1. built tool automation a la Make/Rake/Shovel

  2. invoking commander from a non-interactive shell script

  3. Learning commander

  4. Developing commander functions

    ./ kablammo

This would run your custom-defined "kablammo" function then exit.

#Running an interactive REPL

Commander can also run as a continuous interactive read-eval-print-loop.

./ --repl
> kablammo

This would start the interactive prompt, run your custom-defined kablammo function, then prompt for the next command. Type CTRL-D, "quit", or "q" to quit and return back to your shell.

This mode is good for:

  1. Keeping available in an easily-accessible shell tab
  2. Typing commands without worrying about shell quoting and globbing rules

Making the REPL better with rlwrap

The external "readline wrap" rlwrap utility is highly recommended to add more sophisticated command line editing, history, etc to commander.

  • apt-get install rlwrap on Ubuntu/Debian
  • brew install rlwrap on OS X with Homebrew
  • yum install rlwrap on RPM+yum based linux distros (untested)

Then launch your repl like this:

rlwrap ./ --repl

You can now use the up arrow or CTRL-p to go back in your command history, etc.

Automatic reloading

In repl mode, commander will watch the mtime on the code and configuration files and automatically reload itself when they change. If you add a new command to while a repl is already running, you can immediately use it in the repl. Same thing applies to sites.conf. You can use the commander.engine.addReloader function to watch additional files to trigger reloads.

Integrating commander into your regular shell

(Or: Complete Shell Nirvana)

Here's the mode that will allow you to seamlessly integrate commander commands into your normal interactive bash or zsh. Once you get this set up, my hope is you'll take advantage of the power of python and write more and more of your sophisticated functionality as a commander function instead of a shell function. In this mode you get the best of both worlds of your shell and python.

  • All the normal shell features you love
  • Ability to define simple shell alias and functions in the shell as always
  • Ability to use environment variables (with tab completion, too)
  • Awesome tab completion
  • Awesome command editing
  • Awesome command history and history manipulation
  • filename globbing and pattern expantion
  • Commander integration
    • Specifically your commander commands get all that good stuff, too: tab completion of environment variables, history searching, shell globbing, etc

Source the script from your shell's configuration file (~/.bashrc or ~/.zshrc) like this:

[ -f ~/dotfiles/commander/ ] && source ~/dotfiles/commander/

Now you can turn on commander shell integration by typing commander on

So what does this mean? It means now in your shell when you type a command your shell will look for that command as:

  • a shell built-in command
  • an alias or function
  • an executable file or script on your PATH
  • a commander function

To turn off commander integration with your shell type commander off

To start commander in repl mode, type commander repl. This is good for a dedicated commander window and avoids having to deal with shell quoting and globbing rules.

For now, it is recommended that a new python process be started each time. This makes it easy to do interactive things like prompting for a password securely using the getpass module and so forth. However, commander can also read its input from a file via the --in command line argument. Thus it is possible for the shell to create a fifo, start a single long-running commander python process, and then feed it commands one at a time via the fifo. This avoids any startup/shutdown overhead and makes it easier for your commands to keep state in memory across multiple commands. However, it is not possible to interact with the tty in this mode, so you can't prompt the user for input (at least I haven't found a clean way yet). On most modern systems, the python startup is fast enough to be negligible, so single-command mode is probably sufficient. However, it's on my todo list to figure out a good way to do a background process IPC version.

To start commander shell integration in fifo mode, type commander fifo. Turn it off with commander off

Note that if shell filename globbing is sometimes screwing with your commander arguments when you don't want it to, prefix your command with noglob to disable shell globbing for that single command.

noglob mycommand_that_needs_a_regex  f?art

More info on command_not_found_handler

command_not_found_handler function is a hook that zsh or bash (bash calls it command_not_found_handle though) will call whenever you type in something that doesn't match a target operation (a shell builtin command, an alias, a function, a binary in your PATH, etc). Ever seen an Ubuntu box tell you something like "The program 'cowsay' is currently not installed. The program 'cowsay can be found in the following packages...". That's the command-not-found package using this hook to make that happen.

Built-in support for opening URLs and Applications

commander has built-in support for opening stuff.

  • First, define a task by running the built-in open task
  • You will be prompted for a required task name
    • This can be a simple name or several aliases delimited by a comma
    • Examples: espn or sports,espn
  • You will be prompted for an optional application name
    • Leave this blank to just open URLs with the default application (default browser for web URLs, for images on the filesystem, text editor for code on the filesystem, Finder for directories, etc)
    • Example: "Google Chrome", "Textedit", "HipChat"
  • You will be prompted for arguments
    • These are space-delimited and can be URLs or other program arguments
    • For example: would open both of those URLs in a browser
  • Your data file open.json will be updated to hold this command
  • There is also an open_local command which stores data in open-local.json. I use this for work-related things I need to keep out of my public github dotfiles repository due to NDAs and proprietary info.
  • after the open command completes the new task is immediately available for use

Built-in support for opening web sites


As of commander 2.0 this functionality has been combined into the open command.

Commander includes a sites module which gives you shortcuts to launch or search web sites from the command line. Your mapping of command trigger to URL is stored in a sites.conf configuration file. To set up the sites module:

  • Copy sites_sample.conf to sites.conf and edit it
    • cp commander/sites_sample.conf commander/sites.conf
    • $EDITOR sites.conf
  • read sites_sample.conf for documentation on its syntax

Now you can be hacking away in your shell, type gh (for example) and have open in a new browser tab. Passing in search terms via the query string is also supported by including a %s in the URL where the search terms go.

To add a new site to the configuration file, use the site commander command, so just type "site zefrank" and it will be added to the configuration file and immediately available.

Note that sites.conf supports aliases as the site keywords as well as multiple URLs opened in browser tabs. This is handy for projects. For example, if you have a project "zippio" and you always need your docs, development server, art repo, and github repo open in browser tabs, you can make a site called "zippio" that will open all of those pages in tabs.

Built-in support for OS X Applications


As of commander 2.0 this functionality has been combined into the open command.

Similar to the sites module there is an apps module which gives you the ability to define commander commands to launch Applications on OS X. To set up the apps module:

  • Copy apps_sample.conf to apps.conf and edit it
    • cp commander/apps_sample.conf commander/apps.conf
    • $EDITOR apps.conf
  • read apps_sample.conf for documentation on its syntax

#Built-in support for Keyboard Maestro Macros

Same pattern as above for sites and apps, just called "macros".

Built in commands

  • quit (also CTRL-D or just "q")
    • quits
  • help (also "?")
    • Print a basic help message and a list of loaded commands

Supported Environments

  • Python 2.7 (probably 2.6 will also work)
  • zsh or bash (version 4 or newer)
  • OS X (10.10-10.6, probably the rest of them)
  • Linux

Using Commander over the network

It is pretty straightforward to use the basic unix utilities of fifos and netcat to make commander work across the network. Note this is entirely cleartext and insecure. Also, netcat will only handle one connection at a time.

  • Create a fifo for commander to use
    • mkfifo ~/.commander.fifo
  • Start commander reading commands from that fifo
    • ./ --in ~/.commander.fifo --out ~/.commander.fifo --repl
  • Start a local netcat daemon, sending its output to commander's fifo
    • nc -k -l 1234 > ~/.commander.fifo
  • On a remote machine, use netcat as your commander shell
    • nc 1234
  • Type your commander commands into that nc prompt and they will execute on the remote machine

Keyboard Maestro

I wrote this as a complement to the excellent Keyboard Maestro. I use Keyboard Maestro for lots of UI automation including assinging my most-used apps to the function keys on my macbook and text abbreviations. I highly recommend it. However, Keyboard Maestro does not seem to have the notion of triggering actions by global keystroke followed by typing a keyword/phrase then enter. This is more along the lines of other automation utilities like Spotlight or LaunchBar. You may also want to check out Alfred or Quicksilver. However, I've never been into "adaptive" automation and I like the simplicity and concreteness of knowing I'm typing into a dumb static script that is going to do the same thing every time until I change the code to do something else. That said, those commercial apps have bells and whistles out the wazoo, so if you need iCal integration graphical previews of your PDF files, have a look at them.

My Workflow

I map Command-Space to a Keyboard Maestro macro that actives, positions the window at the top left corner, and resizes it to a small narrow strip. In this window I keep the prompt running in the repl. Thus to access any functionality I want from, I type Command-Space followed by the command such as "unmount", which is a little script that unmounts all my external disks so I can pack up my macbook. I can do this globally no matter where my keyboard focus is at the moment, and the UI is instantaneous.


Commander is licensed under the MIT License

Copyright (c) 2011 Peter Lyons LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.