Awesome Conferences

Bug of the day: getent's surprise

What's wrong with this as a way to turn a hostname into a FQDN?

FQDN=$(getent hosts "$SHORT" | awk '{ print $2 }')

Answer: getent can return multiple lines of results. This only happens if the system is configured to check /etc/hosts before DNS and if /etc/hosts lists the hostname multiple times. There may be other ways this can happen too, but that's the situation that bit me. Of course, there shouldn't be multiple repeated lines in /etc/hosts but nothing forbids it.

As a result you can end up with FQDN="hostname.dom.ain hostname.dom.ain which, and I'm just guessing here, is going to cause problems elsewhere in your script.

The solution is to be a bit more defensive and only take the first line of output:

FQDN=$(getent hosts "$SHORT" | head -1 | awk '{ print $2 }')

Of course, there is still error-checking you should do, but I'll leave that as an exercise to the reader. (Hint: You can check if $? is non-zero; you can also check if FQDN is the null string.)

Technically the man page covers this situation. It says the command "gathers entries" which, being plural, is a very subtle hint that the output might be multiple lines. I bet most people reading the man page don't notice this. It would be nice if the man page explicitly warned the user that the output could be multiple lines long.

P.S. I'm sure the first comment will be a better way to do the same thing. I look forward to your suggestions.

Posted by Tom Limoncelli in Technical Tips

No TrackBacks

TrackBack URL:

6 Comments | Leave a comment

Your command makes a lot of assumptions. It's unclear what your actual intent really is, here. For example, in my /etc/hosts file, I have: hostname

And, "getent hosts hostname" produces that line - it's very similar functionally to "grep '\shostname\s' /etc/hosts".

There's a lot of things to be careful about with your approach ...

Why not use 'host'? I believe that it is likely more portable than 'getent'.

host foo | awk '{print $1}'

Indeed, 'getent' isn't on my workstation at home:

: mac; which getent
: mac; uname -a
Darwin 12.2.0 Darwin Kernel Version 12.2.0: Sat Aug 25 00:48:52 PDT 2012; root:xnu-2050.18.24~1/RELEASE_X86_64 x86_64
: mac; 

Host's output MAY include multiple lines if there are other DNS RR's for that host (MX records or what not), so piping through tail or doing something like:

host foo | awk '{print $1; exit}'

is probably still advised.

Then again, sometimes things just don't turn out the way I expect:

: well; getent hosts www www web
: well; host www
Host www not found: 3(NXDOMAIN)
: well; host has address
: well; 

(I wonder what the output would be if 'www' was listed before '' in the /etc/hosts file?)

Does the hostname command not do this for you in a single command?:

$ hostname --fqdn

"hostname -f" works fine for the host itself but not for looking up other hosts.

IMHO if you're going to use awk then skip the pipe to head otherwise I'd use cut.

echo -e 'foo bar\nbar foo' | awk 'NR==1{print $1}'

But either or, most just pointing it out to make conversation.

I don't understand what you're trying to do.

If you want to find the FQDN of a host, then it's just the concatenation of $SHORT and the domainname:

echo $SHORT.$(hostname -d)

If you want to lookup $SHORT in /etc/hosts, I think you have to be more defensive than you are already - because it's possible for $SHORT to be repeated but for different addresses and different domains. For example, imagine that /etc/hosts contains: www www

then "getent hosts www" returns both entries with all hostname / FQDN combinations, all of which are equally correct if you can't specify the domain you want (and if you can, then your question degenerates to the trivial case above).

So, you should probably pipe the getent through "sort -u", check that you only have one answer, and if not raise an error.

Apologies if I've got the wrong end of the stick here.

Leave a comment