in zsh, I can autocomplete hostnames from /etc/hosts, ie:

ssh f<TAB>

will offer completions for hosts starting with f.

This is configured in /usr/share/zsh/functions/Completion/Unix/_hosts:

local ipstrip='[:blank:]#[^[:blank:]]#'

zstyle -t ":completion:${curcontext}:hosts" use-ip && useip=yes
[[ -n $useip ]] && ipstrip=
if (( ${+commands[getent]} )); then
  _cache_hosts=(${(s: :)${(ps:\t:)${(f)~~"$(_call_program hosts getent hosts 2>/dev/null)"}##${~ipstrip}}})
  _cache_hosts=(${(s: :)${(ps:\t:)${${(f)~~"$(</etc/hosts)"}%%\#*}##${~ipstrip}}})


_hosts=( "$_cache_hosts[@]" )

however, it only works if /etc/hosts file has the format 'IP' 'hostname', ie:      foo.mydomain.com

it will not work if IP is missing:


How can I modify the completion script, so that hostnames without IP are also completed?

Completion of hostnames without IP from /etc/hosts works fine in bash_completion. So I am just trying to get the same behavior on zsh.

  • 1
    What makes you think that /etc/hosts and the output of getent hosts actually work like that?
    – JdeBP
    Jul 6, 2017 at 18:04
  • I put all the hostnames I want to complete into ~/.hosts and then set zstyle -e ':completion:*' hosts 'reply=($(< ~/.hosts))'
    – thrig
    Jul 6, 2017 at 19:06
  • 3
    The express purpose of /etc/hosts is to associate IP addresses with hostnames. Each line (unless empty or a comment) must have one IP address followed by at least one hostname (the first one should be the canonical hostname, the others are aliases). Lines without IP addresses are not valid.
    – Adaephon
    Jul 6, 2017 at 20:41
  • 2
    If you disable HashKnownHosts in your ssh configuration (~/.ssh/config or /etc/ssh/ssh_config), than zsh can also use the entries in ~/.ssh/known_hosts for completion (You may have to remove the the current entries in order to repopulate it with plain text host names).
    – Adaephon
    Jul 6, 2017 at 20:48

3 Answers 3


I'd recommend doing this, which would use your (and the system's) ssh known hosts file instead:

zstyle -e ':completion:*:(ssh|scp|sftp|rsh|rsync):hosts' hosts 'reply=(${=${${(f)"$(cat {/etc/ssh_,~/.ssh/known_}hosts(|2)(N) /dev/null)"}%%[# ]*}//,/ })'

If you're still wanting to use /etc/hosts instead:

zstyle -e ':completion:*:(ssh|scp|sftp|rsh|rsync):hosts' hosts 'reply=(${(s: :)${(ps:\t:)${${(f)~~"$(</etc/hosts)"}%%\#*}##${~strip}}})'

Best of luck!

  • No thanks, I don't want to use known hosts file. And regarding the second solution, how can I achieve that by modifying '/usr/share/zsh/functions/Completion/Unix/_hosts` ? I don't want to put this into my .zshrc. Jul 13, 2017 at 12:14
  • You can replace the line _cache_hosts=(${(s: :)${(ps:\t:)${(f)~~"$(_call_program hosts getent hosts 2>/dev/null)"}##${~ipstrip}}}) in _hosts with _cache_hosts=(${(s: :)${(ps:\t:)${${(f)~~"$(</etc/hosts)"}%%\#*}##${~ipstrip}}}) I believe to achieve the desired effect. Jul 13, 2017 at 16:03

Add a filter function to /usr/share/zsh/functions/Completion/Unix/_hosts:

provide_missing_ip() {
   while read x ; do 
      set -- $x
      if [ ! "$1" ] || [ "${1%%[^#]*}" ] || [ "$2" ] ; then
          echo "$x"
          ip=`dig +short $2`
          [ "$ip" ] || ip=""
          printf "%s\t%s\n" $1 $2

How it works:

  1. If there's two entries, or a blank, or a comment, just output those.
  2. If there's just a hostname, try to provide an IP address with dig.
  3. Failing that, (which shouldn't happen, but let's say it's an intentionally bogus hostname for testing purposes), use a harmless dummy IP address of "", (see Bandrami's answer to "IP address that is the equivalent of /dev/null").

Use the new function to parse the output of getent or < /etc/hosts in the troublesome completion code:

if (( ${+commands[getent]} )); then
  _cache_hosts=(${(s: :)${(ps:\t:)${(f)~~"$(_call_program hosts getent hosts 2>/dev/null | provide_missing_ip )"}##${~ipstrip}}})
  _cache_hosts=(${(s: :)${(ps:\t:)${${(f)~~"$(provide_missing_ip </etc/hosts)"}%%\#*}##${~ipstrip}}})

The hosts file must be in the format IP_address canonical_hostname [aliases...], so your approach should be to fix your hosts file, not to get one program snippet working around your broken file. Other programs may run in problems too, if your hosts is broken, so this would be the only sane solution to your use case. If it's a dynamical IP you are targeting, and you do not want to use any of the better workarounds provided above, consider writing a script which dynamically changes your hosts according to this changing address.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.