VisiData is command-driven, which means that it only does something when you tell it to. Otherwise, it just sits there, waiting for your next command.

Every command is a discrete unit of behavior that does a defined task and runs to completion. Functions that could take longer than a couple hundred milliseconds should run in their own thread (see Threads).

Every command should be reproducible: given the same sheet, cursor position, and input string (if relevant), a command should yield identical output (with a few obvious exceptions, like :kbd:random-rows).

Any command which makes changes to a saveable sheet is appended to that sheet’s Command Log. Since all state changes must be initiated by a reproducible command, this command log can be replayed.

Adding new commands is a natural way to extend VisiData’s functionality.

Command Overview

Commands and Keybindings are managed in a similar way to options. The same precedence hierarchy is used, so that commands can be created or overridden for a specific type of sheet, or even a specific sheet itself.

Since all Sheets ultimately inherit from BaseSheet, a command on BaseSheet is effectively a global command.

Command Context

The execstr is a string of Python code passed to exec() when the command is run.

exec() looks up symbols in this order:

  • the current sheet

  • the vd object

  • the visidata module (see addGlobals() and getGlobals() below)

The vd and sheet symbols are available to specify explicitly.


Unqualified options in a command execstr will use the sheet-specific options context for the current sheet.


In an execstr, while you can get an attribute on vd or sheet without specifying the object, to set an attribute does require an explicit object. e.g. instead of cursorRowIndex=2, it must be sheet.cursorRowIndex=2.

Commands API

visidata.BaseSheet.addCommand(keystrokes, longname, execstr, helpstr='', **kwargs)

Add a new command to cls sheet type.

  • keystrokes: default keybinding, including prefixes.

  • longname: name of the command.

  • execstr: Python statement to pass to exec()’ed when the command is executed.

  • helpstr: help string shown in the Commands Sheet.


  • Use “^X” for Ctrl+X.

  • Primarily, plugin authors and users should use 0-9, “KEY_F(1)”, ALT+ for custom keybindings; these are purposefully left available for user keybindings.

  • Consider not providing a default at all, for infrequently used commands.

  • Instead give it an easy and memorable longname, and/or a unique helpstr which can be searched for in the Command Help (g Ctrl+H) with g/.

  • Many other keycodes can be returned from the curses library as strings.

  • To discover what to use for some unknown key, press that key in VisiData and use the keystroke shown in the status bar.

vd.allPrefixes = ['g', 'z', '^[']

vd.allPrefixes is a list of prefixes (keystrokes that don’t trigger the end of a command sequence). New prefixes can be added to this list, and then they can also be used as prefixes in keybindings.


Combinations of prefixes are allowed, but only in the specified order: g must come before z, which must come before ALT.


ALT is a just a handy constant for “^[”, which represents Ctrl+[, which maps to Esc in the terminal. Curses represents Alt+X (Meta+X on some keyboards) as Esc x. So to bind a command to Alt+X, use ALT+'x' or '^[x'.

Other helpful functions

visidata.vd.bindkey(keystrokes, longname)

Bind keystrokes to longname on BaseSheet and unbind more-specific bindings of keystrokes.

visidata.BaseSheet.bindkey(keystrokes, longname)

Bind keystrokes to longname on the cls sheet type.

visidata.BaseSheet.execCommand(self, cmd, vdglobals=None, keystrokes=None)
visidata.addGlobals(*args, **kwargs)

Update the VisiData globals dict with items from args and kwargs, which are mappings of names to functions. Importers can call addGlobals(globals()) to have their globals accessible to execstrings.


Return the VisiData globals dict.

Rules for command longnames

  1. 3 words max, 2 words if possible. A longname should be short and fit on a keymap.

  2. Command classes should be unique in their first 3 chars and ideally mostly in their first 2.

  3. Command longnames should be intuitively understandable, or at least not jargony.

  4. Longnames should evoke interface, when possible.

  5. Use existing verb-object-input structure if possible:


  • open: push new sheet

  • jump: push existing sheet

  • dup: push copy of sheet

  • show: display on status

  • go: move the cursor

  • scroll: change the visible screen area without changing the cursor

  • addcol: add new column to this sheet

  • setcol: set selected cells in this column

  • search: search one or more columns

  • searchr: search reverse

  • select/unselect/stoggle: add/remove rows from selected rows

  • syscopy/syspaste: copy/paste to system clipboard

  • sysopen: open with $EDITOR or other external program


  • -all: all sheets or all visible columns

  • -col: cursorCol

  • -cols: all visible columns

  • -cells: this column, selected rows

  • -selected: selected rows


  • -expr: python expression

  • -regex: python regex

Many others are used, see the full command list for inspiration.


def show_hello(sheet):

# `sheet` members and `vd` members are available in the execstr scope
BaseSheet.addCommand(None, 'show-hello', 'show_hello()', 'show a warm greeting')

# bind Shift+H, Ctrl+H, and Alt+H to this command
BaseSheet.bindkey('H', 'show-hello')
BaseSheet.bindkey('^H', 'show-hello')
BaseSheet.bindkey(ALT+'h', 'show-hello')