Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I defer shell completion to another command in bash and zsh?

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.

like image 766
Jacob Gillespie Avatar asked Apr 01 '19 01:04

Jacob Gillespie


1 Answers

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
like image 55
Nathan Vērzemnieks Avatar answered Nov 04 '22 01:11

Nathan Vērzemnieks