Optimizing Zsh SSH Command Completion: Integrating Config, Known_Hosts and System Hosts


1 views

By default, zsh's SSH completion has several shortcomings when it comes to practical usage:

  • Ignores ~/.ssh/config host definitions
  • Requires username specification before host completion
  • Presents fragmented completion lists from different sources

Add this to your ~/.zshrc to enable better completion:

# Improved SSH completion setup
zstyle ':completion:*:(ssh|scp|sftp):*' hosts off
zstyle ':completion:*:(ssh|scp|sftp):*' users off

_ssh_config_hosts() {
  local configfile
  local -a hosts
  for configfile in ~/.ssh/config(N) /etc/ssh/ssh_config(N); do
    [ -r $configfile ] && hosts+=(${${${(@M)${(f)"$(< $configfile)"}:#Host *}#Host }:#*[*?]*})
  done
  print -u2 -l $hosts
}

zstyle -e ':completion:*:(ssh|scp|sftp):*' hosts 'reply=(
  ${=${${(f)"$(_ssh_config_hosts 2>/dev/null)"}:#*[*?]*}}
  ${=${${${(f)"$(cat ~/.ssh/known_hosts{,2} /etc/hosts 2>/dev/null)"}%%\ *}%%,*}
)'

This solution combines multiple host sources:

  1. Parses ~/.ssh/config and /etc/ssh/ssh_config for Host entries
  2. Includes known_hosts (and known_hosts2 if exists)
  3. Incorporates system /etc/hosts entries

For username completion from config:

zstyle ':completion:*:(ssh|scp|sftp):*' users \
  $(awk '/^Host / {print $2}' ~/.ssh/config | xargs -n1 ssh -G 2>/dev/null | awk '/^user / {print $2}')

After reloading your zsh (exec zsh), test with:

ssh [TAB][TAB]

You should now see a unified list of all available hosts.


When working with ZSH's default SSH completion, you'll notice several pain points:

# Current behavior example:
$ ssh [TAB]
user1@
user2@
# Then need to press TAB again to see hosts

Key issues include:
- No integration with ~/.ssh/config Host entries
- Forces username-first completion
- Separates host sources into multiple lists

We want a unified completion that:

  1. Reads Host entries from ~/.ssh/config
  2. Includes known_hosts entries
  3. Incorporates /etc/hosts aliases
  4. Shows usernames as optional suggestions
  5. Displays everything in one list

Add this to your .zshrc:

# Enhanced SSH completion for ZSH
zstyle ':completion:*:(ssh|scp|sftp):*' hosts \
    ${${${(@M)${(f)"$(<~/.ssh/config)"}:#Host *}#Host }:#*[*?]*}

zstyle ':completion:*:(ssh|scp|sftp):*' users \
    $(awk '/^Host / {print $2}' ~/.ssh/config | sort -u)

zstyle ':completion:*:(ssh|scp|sftp):*' known-hosts-files \
    ~/.ssh/known_hosts /etc/ssh/ssh_known_hosts

# Combine all sources
zstyle ':completion:*:(ssh|scp|sftp):*' tag-order \
    'hosts:-host hosts:-user:host users hosts:-domain hosts:-ipaddr' \
    'hosts:local-host hosts:local-domain hosts:remote-host hosts:remote-domain'

The configuration works by:

1. Extracting Host entries from SSH config:
   ${${${(@M)${(f)"$(<~/.ssh/config)"}:#Host *}#Host }:#*[*?]*}

2. Adding known_hosts files:
   ~/.ssh/known_hosts and system-wide /etc/ssh/ssh_known_hosts

3. Merging completion sources with proper ordering:
   tag-order controls the display sequence

For power users, consider adding:

# Include /etc/hosts entries
local -a hosts
hosts=($(awk '/^[^#]/ {print $2}' /etc/hosts))
zstyle ':completion:*:hosts' hosts $hosts

# Add AWS/GCP host completion (if using those clouds)
if [[ -f ~/.aws/config ]]; then
    zstyle ':completion:*:hosts' aws \
        $(aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==Name].Value[]' --output text)
fi

After reloading your shell (exec zsh), test with:

$ ssh [TAB]
# Should now show combined list including:
# - Host entries from ~/.ssh/config
# - Entries from known_hosts
# - /etc/hosts aliases
# - Usernames (optional)