Skip to content

About

Fast tab completion for the stock macOS bash 3.2, written in C.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

146 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

autocomplete: fast tab completion for macOS bash 3.2

Fast tab completion for the stock macOS bash 3.2 (/bin/bash), written in C.

macOS still ships bash 3.2 from 2007, and most modern completion tools have dropped support for it. This project was built specifically for that bash. All completion logic runs in a single compiled C binary, so pressing TAB doesn't run any shell code. Completion is instant and new terminals open as fast as before.

$ docker run --network <TAB>
bridge   host   none   my_app_default
$ git checkout fe<TAB>
$ git checkout feature/login
$ make in<TAB>
$ make install

It is a lightweight alternative to bash-completion that completes docker, git, kubectl, make, mvn, npm and more. You don't need Homebrew bash, the bash-completion package or zsh, and there are no shell scripts. It shines on parameter monsters like curl and wget. Hundreds of flags are grouped to fit your screen, and TAB on an empty argument shows ready-made command snippets.

Why another bash completion tool?

If you use bash on a Mac, you've probably found that completion tools don't support it:

  • bash-completion 2.x requires bash 4.2 or newer.
  • bash-completion 1.3, the last version that runs on bash 3.2, is unmaintained. It loads every completion script at shell startup, which makes each new terminal slow to open.
  • The usual workaround is to brew install bash and change your login shell.

autocomplete works with the bash already on your Mac:

  • Built for macOS bash 3.2. Tested against Apple's /bin/bash and Apple clang.
  • Fast because it's all C. Bash registers the binary with complete -C, so every TAB calls the binary directly. No bash functions, no sed/awk pipelines, no subshells.
  • Fast shell startup. The init line registers commands and loads nothing else. Specs are read only when you press TAB.
  • No dependencies. One binary, plain C, builds with the Xcode Command Line Tools.

Snippets: ready-made commands on TAB

Most completion tools help you spell a flag you already know. Snippets help when you don't remember which flags you need. Press TAB right after a command and you get a list of complete, working commands, each with a one-letter key and a short explanation:

$ curl <TAB>
πŸ“Œ @d  -- curl -d 'name=ann&age=30' https://httpbin.org/post   πŸ“š POST form fields
πŸ“Œ @j  -- curl --json '{"id":1}' https://httpbin.org/post      πŸ“š POST JSON
πŸ“Œ @l  -- curl -L -o page.html https://example.org             πŸ“š follow redirects, save to file
πŸ”₯ @x  -- curl -X DELETE https://httpbin.org/delete            πŸ“š any other method: PUT, PATCH, DELETE
...
$ curl @x<TAB>
$ curl -X DELETE https://httpbin.org/delete

Type @ plus the key and press TAB, and the whole command is inserted for you to edit. πŸ“Œ marks the most used ones, which are listed first. When TAB already completes something else there (files for tar, hosts for ssh), type @<TAB> to see the snippets.

Bundled snippets cover the commands whose flags are hard to remember:

  • curl, wget: POST JSON or form data, uploads, headers only, redirects, resuming
  • ps, lsof: top CPU/memory processes, who listens on a port, open files of a process
  • find, grep: recently changed files, large files, recursive search with excludes
  • tar: create, extract, list, exclude folders, strip the top folder
  • zip, unzip, unrar: zip a folder without macOS junk, exclude folders, passwords, split archives, .app bundles with symlinks, extract into a folder, multi-part RARs; gzip/gunzip, xz/unxz, bzip2/bunzip2, compress/uncompress: keep the original, read without unpacking, all CPU cores for xz, .tar.* via pipes
  • ssh: port forwarding, jump hosts, SOCKS proxy, remote commands
  • jq: pretty-print, pick and filter fields, CSV, shell variables
  • jfr: hot methods, GC pauses, allocations from a Java flight recording
  • brew: dependency tree (all installed or one formula), what depends on a formula, leaves, unused dependencies, cleanup, Brewfile backup
  • docker system/image/network/container/volume prune, docker compose config
  • macOS defaults (hidden Finder, Dock and screenshot settings), sips (resize, convert HEIC), pbcopy, pbpaste, say, duti, mdls
  • Shell keywords for, while, until, case, if: practical one-liners (if git <TAB> still completes git, and so do then/do/else/elif)

Add your own with a snippets: list in any spec (format).

Other tools don't do this inside TAB completion. bash-completion, zsh and fish complete flags and values only. Separate cheat-sheet tools like navi or tldr show examples, but in their own window or command, not on TAB at the prompt you're already typing on.

Features

  • Bundled completions for docker, git, kubectl, make, mvn, java, jfr, python, node, npx, bun, bunx, npm, yarn, pnpm, pip, go, cargo, gradle, brew, gh, ssh, scp, terraform, helm, aws, az, gcloud, doctl, flyctl/fly, vercel, pulumi, minikube, kind, k9s, kubectx, kubens, ansible, ansible-playbook, psql, pg_dump, mysql, mysqldump, redis-cli, sqlite3, swift, pod, fastlane, mas, ffmpeg, pngquant, bat, fzf, colima, orbctl, pyenv, rbenv, asdf, nvm, tmux, ps, kill, pkill, killall, lsof, find, grep, rg, sed, awk, patch, diff, tar, zip, unzip, unrar, gzip, gunzip, xz, unxz, bzip2, bunzip2, compress, uncompress, help (bash builtins), man (page names from manpath), jq, curl, wget, rsync, openssl, dig, nc, tcpdump, code (VS Code), claude (Claude Code), macOS open, launchctl, defaults, diskutil, say, caffeinate, pbcopy, pbpaste, sips, screencapture, duti, mdls, mdfind, pmset, networksetup, security, codesign, xcrun, plutil, hdiutil, spctl, xattr, softwareupdate, tccutil, scutil, mdutil, osascript, shortcuts, dscl, log, system_profiler, ditto, textutil, afplay, qlmanage, profiles, sw_vers, nettop, fs_usage, and autocomplete itself.
  • Wrapper commands sudo, env, time, xargs complete the command after them with that command's own completion (sudo git chec<TAB> β†’ checkout).
  • Live values from your environment:
    • docker: running containers, images, networks, volumes
    • git: branches, tags, remotes, stashes, changed files
    • kubectl: namespaces, contexts, pods, nodes, resource types
    • npm: package.json scripts and dependencies
    • mvn: profiles and modules from pom.xml, plugin goals for plugins the project uses
    • make: Makefile targets and variables, with descriptions from ## text or the comment above each target (honors make -C dir and make -f file)
    • killall, pkill: your running process names; kill, caffeinate -w: PIDs with process names
    • say: voices with language and sample text (say -v Ed<TAB>), audio devices
    • code: installed extensions for --uninstall-extension/--disable-extension
    • sips: property keys with types for -g/-s; screencapture: app bundle ids for -B
    • duti: extensions with their UTI and current app for -x; app bundle ids for -s (also by app name: duti -s Preview<TAB> β†’ com.apple.Preview), then every UTI known to the system as UTI -- .ext β†’ current app (typing an extension finds its UTIs: duti -s com.apple.Preview md<TAB>; when several match they are listed as ts1: …, ts2: … and ts2<TAB> picks the second; the same works for -d/-l: duti -d md<TAB> β†’ duti -d net.daringfireball.markdown) (β˜… = declared by the app, βœ” = currently opens with it), read live from LaunchServices via osascript (duti -s com.apple.Preview com.ad<TAB> β†’ com.adobe.pdf), then the role
    • mdls: Spotlight attribute names with descriptions for -name
    • ssh, scp: hosts from ~/.ssh/config and known_hosts (scp gr<TAB> β†’ grafana:)
    • brew: installed formulae and services
    • gh: pull requests, issues, workflow runs, gists, labels, releases, repos
    • yarn, pnpm: package.json scripts, dependencies and workspace packages
    • pip: installed packages; go: packages and test names; cargo: bins, examples, tests
    • terraform: resources from state, *.tfvars; helm: releases, repo charts, namespaces
    • node: package.json scripts for --run, test reporters; npx, bunx: binaries in node_modules/.bin
    • bun: scripts, dependencies (remove, why, update), node_modules/.bin for bun x
    • python: module names for -m (python3 -m http<TAB> β†’ http.server)
    • java: source files to run (java <TAB>: *.java files under the cwd declaring main), module names (-m, --add-modules), jars and class paths
    • aws: profiles, regions, S3 buckets; az: subscriptions, resource groups, locations; gcloud: projects, configurations, regions, zones, GCS buckets; tmux: sessions and buffers
    • open: app names (-a) and bundle ids (-b); launchctl: services and LaunchAgent plists
  • Descriptions next to subcommands and flags when several match (git stash <TAB> β†’ pop -- Apply a stash and remove it).
  • Snippets: curl <TAB> lists ready-made commands, curl @x<TAB> inserts one.
  • Handles --flag=value, image:tag, quoted words and file names with spaces.
  • Add a command by writing a small YAML file, with no rebuild (how).
  • Generate flag completion for system commands like ps, ls or tar from their man pages, for other tools from their --help output, and delegate Go/cobra programs (helm, gh, hugo) to their own live completion (details).
  • Built-in completion for SSH hosts, users, PIDs, process names and signals; ~user/ paths.
  • Complete shell aliases like the command they wrap (autocomplete alias kubectl k).
  • Very large specs (over 100 KB) are compiled to a cache, so TAB doesn't re-parse YAML.

Requirements

  • To build from source: the Xcode Command Line Tools (xcode-select --install) on macOS, or gcc/clang and make on Linux.
  • bash 3.2 (the macOS /bin/bash) or any newer bash.

Installation

Prebuilt binaries are published for macOS (Apple silicon and Intel) and Linux (x86_64, arm64).

Homebrew (macOS, Linux):

brew install hza/tap/autocomplete
echo 'eval "$(autocomplete bash)"' >> ~/.bashrc

Install script (no sudo; installs to ~/.local and adds the eval line to ~/.bashrc):

curl -fsSL https://github.com/hza/bash-autocomplete/releases/latest/download/install.sh | sh
# or
wget -qO- https://github.com/hza/bash-autocomplete/releases/latest/download/install.sh | sh

Set PREFIX=/usr/local, PROFILE=~/.bash_profile or VERSION=v0.1.0 for sh to change the defaults (e.g. ... | PREFIX=$HOME/bin sh). The script verifies the download's SHA-256 checksum. Uninstall with ... | sh -s -- --uninstall (same PREFIX/PROFILE).

From source

git clone https://github.com/hza/bash-autocomplete.git autocomplete
cd autocomplete
make install

make install:

  • installs the binary to /usr/local/bin/autocomplete,
  • installs the bundled specs to /usr/local/share/autocomplete/,
  • precompiles any spec over 100 KB into the cache,
  • adds eval "$(autocomplete bash)" to ~/.bashrc, unless it's already there.

Open a new terminal (or run source ~/.bashrc) and press TAB after docker, git or make.

macOS Terminal starts login shells, which read ~/.bash_profile, not ~/.bashrc. make install warns if your ~/.bash_profile doesn't load ~/.bashrc; add [ -f ~/.bashrc ] && . ~/.bashrc to it.

Options:

make install PREFIX=$HOME/.local          # install without sudo (put ~/.local/bin on PATH first)
make install PROFILE=$HOME/.bash_profile  # use a different startup file

If /usr/local isn't writable, use sudo make install. In that case the cache isn't built during install; it's built on your first completion instead.

Using it without installing

make
eval "$(./autocomplete bash)"                     # recommended
source /dev/stdin <<<"$(./autocomplete bash)"     # if you prefer `source`

source <(autocomplete bash) does not work in bash 3.2. A long-standing bash bug makes it silently read nothing. Use one of the forms above.

Uninstalling

make uninstall          # use the same PREFIX / PROFILE you installed with

This removes the binary, the bundled specs, their cache files and the exact eval line from your profile. Your own specs in ~/.config/autocomplete are left untouched.

Commands

Command Description
autocomplete bash Print the bash init script (use with eval).
autocomplete bash --man Same, plus every command in $PATH that has a man page and no completion yet: its flags are read from the man page on the first TAB and cached (see Cache).
autocomplete list List commands that have a spec, one per line with its description. Your own specs in ~/.config/autocomplete get a tag: [override] (replaces a shipped spec; , unchanged if identical to it, so you can delete it), [added] (no shipped spec), [alias -> CMD], plus generated: man|help|cobra and update: NEW > OLD when the shipped spec is newer.
autocomplete list --paths Same, with the spec file each command uses (handy to see which copy wins, or where a bundled spec lives).
autocomplete list --user Only your own specs in ~/.config/autocomplete (generated with add, aliases, hand-written); combines with --paths.
autocomplete manage Same as list --user (takes --paths/--tsv).
autocomplete list --tsv NAME<TAB>DESCRIPTION lines (no padding), for scripts and exec completers.
autocomplete check FILE Validate a spec and print how it was understood (as JSON).
autocomplete add [--force] [--from cobra|man|help] CMD... Write ~/.config/autocomplete/CMD.yaml from the first source that works: cobra bridge, man page, CMD --help.
autocomplete edit NAME Open ~/.config/autocomplete/NAME.yaml in $VISUAL (else $EDITOR, else vi). An existing copy is opened as is; without one, the shipped spec is copied there first. Warns when the shipped spec has a newer version; validates the file after the editor exits; if you saved no changes, the copy is removed (the shipped spec stays in use).
autocomplete rm [-f] NAME... Delete your spec or alias in ~/.config/autocomplete (and its cache), asking [y/n] for each unless -f; the shipped spec, if any, is used again.
autocomplete diff [NAME...] diff -u of your copies in ~/.config/autocomplete against the shipped specs, so you can merge what you want (default: every override that differs; see [override, update: …] in list). Exit 1 if any differ.
autocomplete alias [--force] CMD NAME... Complete NAME (e.g. a shell alias) like CMD by symlinking ~/.config/autocomplete/NAME.yaml to its spec.
autocomplete alias --rm NAME... Remove aliases made by autocomplete alias (only symlinks are removed).
autocomplete alias --list List aliases as NAME -> CMD.

FAQ

How do I get tab completion for git and docker in macOS bash?

Install autocomplete (make install) and open a new terminal. You don't need Homebrew bash, bash-completion or zsh.

Do I need to upgrade bash or switch to zsh?

No. autocomplete is designed for the bash 3.2 that macOS ships as /bin/bash.

Why is it faster than bash-completion?

bash-completion is written in bash and sources its scripts into every shell. autocomplete keeps all logic in a compiled C binary that runs only when you press TAB. Shell startup does nothing beyond one complete registration.

Can I add completion for my own command?

Yes. Write ~/.config/autocomplete/<command>.yaml describing subcommands, flags and values. See Adding your own commands.

Documentation

Building from source

make            # builds ./autocomplete
make clean

The code is plain C with no dependencies, in src/: main.c (subcommands), complete.c (spec walk), output.c (candidate list and display), builtins.c (files, make, $PATH, exec completers), spec.c (spec lookup and cache), yaml.c (YAML reader and writer), manparse.c (man page reader) and util.c. Bundled specs live in specs/, grouped into topic subfolders (vcs/, cloud/, lang/, …); specs are found at any depth.

License

MIT. See LICENSE.

About

Fast tab completion for the stock macOS bash 3.2, written in C.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages