3

I find myself frequently tasked with updating DNS records and needing to know exactly when the refresh will begin (a.k.a TTL expiration). I came up with this bash one-liner that gives me the output I want:

$ dig +noall +answer www.google.com | \
> awk '{ print $2 }' | \
> { read secs; echo "`expr $secs / 60`m `expr $secs % 60`s remaining ($secs)"; }

output:

3m 36s remaining (216)

I'd like to be able to wrap the watch command around this so that I can leave it running in a terminal and get a live countdown. Something like:

$ watch -n 10 "dig +noall +answer www.google.com | \
> awk '{ print $2 }' | \
> { read secs; echo '`expr $secs / 60`m `expr $secs % 60`s remaining ($secs)'; }"

But this simply results in a non-functional watch instance followed by:

expr: syntax error

I am pretty sure this is some kind of piping/quoting problem and I'm not that well-versed on the intricacies. For the sake of portability I'd prefer to keep it as a one-liner vs. resorting to a script that requires saving to disk. Does anyone know the right way to accomplish this?

4 Answers 4

1

First of all you need to avoid using expr. It's rarely used. Instead I've here the version that does exact the same. Not much different than the above:

watch -n1 'dig +noall +answer google.com | \ 
 awk "{print \$2}" | \
 { read secs; echo "$((secs / 60 ))m $((secs % 60))s remaining $((secs))";}'

Another aproach which can be used using tmux or screen

    while sleep 1; do dig +noall +answer google.com | \ 
   { while read -r secs; do echo "$((secs / 60 ))m $((secs % 60))s remaining $((secs)) seconds"; done < <(awk '{print $2}' | \
   head -n1 ); };  done

Good luck.

4
  • Thank you so very much for this! (and to the others who posted answers). Works a treat!
    – luckman212
    Jul 12, 2013 at 17:11
  • The double parentheses around $secs at the end serve no purpose. Jul 13, 2013 at 4:20
  • @DennisWilliamson, Since arithmetic operations in bash happens within (()) and since $(()) evaluates an arithmetic operations, using $((secs)) isn't that weird. When it comes to $((secs)) of course we could have written it as in $secs. None would have heart in this case. $((secs)) is probably more a habit rather than style. Jul 13, 2013 at 12:06
  • time { a=345; for i in {0..1000000}; do echo "$((a))"; done > /dev/null; } is about 20% faster if you remove the parentheses. Jul 13, 2013 at 20:10
1

Why not let AWK do the math and printing?

watch -n 10 'dig +noall +answer google.com | awk '\''{print int($2 / 60) "m", $2 % 60 "s remaining", "(" $2 ")"; exit}'\'''
1

The syntax error problem, because you forgot to escape the backstick character, the double quote character. And remember to escape dollar sign also, for the value of $secs is passed to expr, if you don't, there is no output to stdout:

watch -n 10 "dig +noall +answer www.google.com | \
awk '{print\$2}' | \
{ read secs; echo \"\`expr \$secs / 60\`m \`expr \$secs % 60\`s remaining (\$secs)\"; }"
0

This seems to work for me.

watch -n 10 "dig +noall +answer www.google.com | \
 awk '{print \$2}' | \
 { read secs; echo \"\$((\$secs / 60))m \$((\$secs % 60))s remaining\"; }"

or alternatively quoted to avoid quite that much mess:

watch -n 1 'dig +noall +answer www.google.com | \
 awk "{print \$2}" | \
 { read secs; echo "$(($secs / 60))m $(($secs % 60))s remaining"; }'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .