14

I am attempting to write a shell script utility that wraps other shell utilities into a single CLI and am trying to get shell completion to work in zsh and bash.

For example, let's say the CLI is named util:

util aws [...args] #=> runs aws
util docker [...args] #=> runs docker
util terraform [...args] #=> runs terraform

What I would like, ideally, is a way in zsh and bash completion to be able to say "complete this subcommand X like other command Y" independently from the implementation of the completion for the wrapped scripts.

Something like:

compdef 'util aws'='aws'
compdef 'util docker'='docker'
compdef 'util terraform'='terraform'

A stretch goal would be to allow for completion of an arbitrary sub-command to a subcommand in another binary:

util aws [...args] #=> completes against `aws`
util ecr [...args] #=> completes against `aws ecr`

Is any of this possible? I've been attempting to emulate the completion scripts of the individual binaries, however there is significant variation in how other completion scripts are written.

Jacob Gillespie
  • 3,981
  • 3
  • 23
  • 33
  • 2
    You better ask 2 questions for bash and zsh. Don't expect one to know both well. –  Apr 01 '19 at 04:05
  • 1
    Add OS or Linux Distribution and version to your question. – Cyrus Apr 01 '19 at 04:22
  • @Cyrus - its a question about bash or zsh, which are portable across OS or distribution. – Jacob Gillespie Apr 01 '19 at 14:02
  • 3
    Probably worth investigating how zsh handles completion for `sudo`. In my case, `sudo foo ` shows completions for `foo`, usually. Maybe a lazy way out would be to `compdef util=sudo` – muru Apr 10 '19 at 02:35

1 Answers1

5

I know nothing about zsh, but I can offer a solution for bash. It delegates using the _complete function (which I found following muru's suggestion - good call!).

The second section of the function provides completions for the util command itself, which I assume here will just be a list of subcommands. You can tailor that to your needs, of course.

The first section handles the delegation in the case where a full subcommand has been typed, and optionally a target for completion according to the subcommand's completion.

The function

_delegate() {
  local cur subs
  cur="${COMP_WORDS[COMP_CWORD]}" # partial word, if any
  subs="ssh aws docker terraform"
  if [[ $COMP_CWORD == 2 ]]; then
    # Two whole words before the cursor - delegate to the second arg
    _command $2
  else
    # complete with the list of subcommands 
    COMPREPLY=( $(compgen -W "${subs}" -- ${cur}) )
  fi
}

Installation

njv@pandion:~$ complete -F _delegate util

Demo

1d [njv@eidolon:~] $ util
aws        docker     ssh        terraform
1d [njv@eidolon:~] $ util ssh
::1                        gh                         ip6-localhost              ubuntu.members.linode.com
eidolon                    github.com                 ip6-loopback
ff02::1                    ip6-allnodes               localhost
ff02::2                    ip6-allrouters             ubuntu
1d [njv@eidolon:~] $ util ssh ip6-
ip6-allnodes    ip6-allrouters  ip6-localhost   ip6-loopback
Nathan Vērzemnieks
  • 5,495
  • 1
  • 11
  • 23
  • I believe your "stretch goal" is possible as well, but I'm still trying to work out a good approach. I'll update my answer if I find one soon :) – Nathan Vērzemnieks Apr 13 '19 at 19:05