Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to auto-complete a multi-level command aliased to a single command?

Say I have two bash functions:

dock() { sudo docker $@ ;}

and

dock-ip() { sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' $@ ;}

How to get bash auto-completion working with the second function?

With the first one, it is as easy as adding:

_completion_loader docker; complete -F _docker dock

This will not work for the second one. The autocomplete source for Docker is in /usr/share/bash-completion/completions/docker on Debian Stretch. I have more functions like dock-run, dock-exec, etc. so I don't want to write a custom completion function for each of them.

Also, complete -F _docker_container_inspect dock-ip only partially works; tab only lists containers, not completes partial strings.


Research:

How do I autocomplete nested, multi-level subcommands? <-- needs custom functions

https://superuser.com/questions/436314/how-can-i-get-bash-to-perform-tab-completion-for-my-aliases <-- automated for top commands only

like image 384
Drakes Avatar asked Dec 12 '17 21:12

Drakes


1 Answers

With a combined hour of bash completion experience, I took apart the docker completion script (/usr/share/bash-completion/completions/docker) and the bash_completion.sh script to come up with a wrapper function:

# Usage:
#   docker_alias_completion_wrapper <completion function> <alias/function name>
#
# Example:
#   dock-ip() { docker inspect --format '{{ .NetworkSettings.IPAddress }}' $@ ;}
#   docker_alias_completion_wrapper __docker_complete_containers_running dock-ip
function docker_alias_completion_wrapper {
  local completion_function="$1";
  local alias_name="$2";

  local func=$(cat <<EOT
    # Generate a new completion function name
    function _$alias_name() {
        # Start off like _docker()
        local previous_extglob_setting=\$(shopt -p extglob);
        shopt -s extglob;

        # Populate \$cur, \$prev, \$words, \$cword
        _get_comp_words_by_ref -n : cur prev words cword;

        # Declare and execute
        declare -F $completion_function >/dev/null && $completion_function;

        eval "\$previous_extglob_setting";
        return 0;
    };
EOT
  );
  eval "$func";

  # Register the alias completion function
  complete -F _$alias_name $alias_name
}
export -f docker_alias_completion_wrapper

I then created my alias/functions like this:

# Get container IP
dock-ip() { docker inspect --format '{{ .NetworkSettings.IPAddress }}' $@ ;}
docker_alias_completion_wrapper __docker_complete_containers_running dock-ip

# Execute interactive container
dock-exec()  { docker exec -i -t --privileged $@ ;}
docker_alias_completion_wrapper __docker_complete_containers_all dock-exec

... 

Be sure to call _completion_loader docker; at the top of your profile aliases script to load the main Docker completion scripts. I invite more skilled bash programmers to improve this answer, please.

like image 87
Drakes Avatar answered Nov 01 '22 05:11

Drakes