How to edit command completion for ssh on zsh?

Solution 1:

Here's the relevant part from my .zshrc. It hasn't changed since 2002, so I might write it differently today, but it still works to complete host names from ~/.ssh/config and ~/.ssh/known_hosts (if HashKnownHosts is off — it didn't exist in those days).

h=()
if [[ -r ~/.ssh/config ]]; then
  h=($h ${${${(@M)${(f)"$(cat ~/.ssh/config)"}:#Host *}#Host }:#*[*?]*})
fi
if [[ -r ~/.ssh/known_hosts ]]; then
  h=($h ${${${(f)"$(cat ~/.ssh/known_hosts{,2} || true)"}%%\ *}%%,*}) 2>/dev/null
fi
if [[ $#h -gt 0 ]]; then
  zstyle ':completion:*:ssh:*' hosts $h
  zstyle ':completion:*:slogin:*' hosts $h
fi

Solution 2:

The function that provides ssh completion is located at /usr/share/zsh/functions/Completion/Unix/_ssh on my system.

Also see man zshcompsys for documentation (especially do a search on "host" which appears in multiple places and "ssh" which appears in a couple of places).

It may be possible that adding a zstyle command to your ~/.zshrc would do what you're looking for without having to modify the completion function.

Solution 3:

I do this using a list of all hosts on a given domain using dig. You can replace the function below with whatever lookup system you want including your hosts file or a static list:

function complete_host_from_zone () {
    reply=(`dig axfr ouraynet.com @ns1.ouraynet.com | grep -e '^[a-z]' | cut -d\. -f1`)
}
compctl -x 'p[1]' -K complete_host_from_zone -- ssh

Note: The code above might not fully replace the complete system for the ssh command in your configuration. If you have problems with it, try changing the "ssh" command to some other random command like "mycompletetest" and see if the completion works for that.

Also note that this does the dns zone transfer on every completion! If you use this a lot or on a fairly static domain it would make sense to do the lookup and save the result, then in your lookup function just set reply=zone_result.