Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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?

share|improve this question

4 Answers 4

up vote 1 down vote accepted

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.

share|improve this answer
    
Thank you so very much for this! (and to the others who posted answers). Works a treat! –  luckman212 Jul 12 '13 at 17:11
    
The double parentheses around $secs at the end serve no purpose. –  Dennis Williamson Jul 13 '13 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. –  val0x00ff Jul 13 '13 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. –  Dennis Williamson Jul 13 '13 at 20:10

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}'\'''
share|improve this answer

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)\"; }"
share|improve this answer

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"; }'
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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