As part of its programmable completion suite, Bash includes hostname
completion. This completion mode reads hostnames from a
file in hosts(5)
format to find possible completions matching the
current word. On Unix-like operating systems, it defaults to reading the file
in its usual path at /etc/hosts
.
For example, given the following hosts(5)
file in place at /etc/hosts
:
127.0.0.1 localhost
192.0.2.1 web.example.com www
198.51.100.10 mail.example.com mx
203.0.113.52 radius.example.com rad
An appropriate call to compgen
would yield this output:
$ compgen -A hostname
localhost
web.example.com
www
mail.example.com
mx
radius.example.com
rad
We could then use this to complete hostnames for network diagnostic tools like
ping(8)
:
$ complete -A hostname ping
Typing ping we
and then pressing Tab would then complete to ping
web.example.com
. If the shopt
option hostcomplete
is on, which
it is by default, Bash will also attempt host completion if completing any word
with an @
character in it. This can be useful for email address completion or
for SSH username@hostname
completion.
We could also trigger hostname completion in any other Bash
command line (regardless of complete
settings) with the Readline shortcut
Alt+@ (i.e. Alt+Shift+2). This works even if hostcomplete
is turned off.
However, with DNS so widely deployed, and with system /etc/hosts
files
normally so brief on internet-connected systems, this may not seem terribly
useful; you’d just end up completing localhost
, and (somewhat erroneously) a
few IPv6 addresses that don’t begin with a digit. It may seem even less useful
if you have your own set of hosts in which you’re interested, since they may
not correspond to the hosts in the system’s /etc/hosts
file, and you probably
really do want them looked up via DNS each time, rather than maintaining static
addresses for them.
There’s a simple way to make host completion much more useful by defining the
HOSTFILE
variable in ~/.bashrc
to point to any other file containing a list
of hostnames. You could, for example, create a simple file ~/.hosts
in your
home directory, and then include this in your ~/.bashrc
:
# Use a private mock hosts(5) file for completion
HOSTFILE=$HOME/.hosts
You could then populate the ~/.hosts
file with a list of hostnames in which
you’re interested, which will allow you to influence hostname completion
usefully without messing with your system’s DNS resolution process at all.
Because of the way the Bash HOSTFILE
parsing works, you don’t even
have to fake an IP address as the first field; it simply scans the file for any
word that doesn’t start with a digit:
# Comments with leading hashes will be excluded
external.example.com
router.example.com router
github.com
google.com
...
You can even include other files from it with an $include
directive!
$include /home/tom/.hosts.home
$include /home/tom/.hosts.work
This really surprised me when reading the source, because I
don’t think /etc/hosts
files generally support that for their usual name
resolution function. I would love to know if any systems out there actually do
support this.
The behaviour of the HOSTFILE
variable is a bit weird; all of the hosts from
the HOSTFILE
are appended to the in-memory list of completion hosts each
time the HOSTFILE
variable is set (not even just changed), and host
completion is attempted, even if the hostnames were already in the list. It’s
probably sufficient just to set the file once in ~/.bashrc
.
This setup allows you to set hostname completion as the default method for all
sorts of network-poking tools, falling back on the usual filename completion if
nothing matches with -o default
:
$ complete -A hostname -o default curl dig host netcat ping telnet
You could also use hostname completions for ssh(1)
, but to account for
hostname aliases and other ssh_config(5)
tricks, I prefer to read
Host
directives values from ~/.ssh/config
for that.
If you have machine-readable access to the complete zone data for your home or
work domain, it may even be worth periodically enumerating all of the hostnames
into that file, perhaps using rndc dumpdb -zones
for a BIND9 setup, or using
an AXFR
request. If you have a locally caching recursive nameserver, you
could even periodically examine the contents of its cache for new and
interesting hosts to add to the file.