With dd you can reliably read a single byte from a file. With stty you can set a min number of bytes to qualify a terminal read and a time out in tenths of a second. Combine those two and you can do without sleep entirely, I think, and just let the terminal's read timeout do the work for you:
s=$(stty -g </dev/tty) (while stty raw -echo isig time 20 min 0;test -z "$( dd bs=1 count=1 2>/dev/null; stty "$s")" || (exec sh) do echo "$SECONDS:" do your stuff here maybe echo no sleep necessary, I think [ "$((i+=1))" -gt 10 ] && exit done ) </dev/tty
That is a little example while loop that I mocked-up for you to try out. Every two seconds dd times out on its attempted read of stdin - redirected from /dev/tty - and the while loop loops. That or dd doesn't time-out because you press a key - in which case an interactive shell is invoked.
Here is a test run - the numbers printed at the head of each line is the value of the shell variable $SECONDS:
273315: do your stuff here maybe no sleep necessary, I think 273317: do your stuff here maybe no sleep necessary, I think 273319: do your stuff here maybe no sleep necessary, I think 273321: do your stuff here maybe no sleep necessary, I think sh-4.3$ : if you press a key you get an interactive shell sh-4.3$ : this example loop quits after ten iterations sh-4.3$ : or if this shell exits with a non-zero exit status sh-4.3$ : and speaking of which, to do so you just... sh-4.3$ exit exit 273385: do your stuff here maybe no sleep necessary, I think 273387: do your stuff here maybe no sleep necessary, I think 273389: do your stuff here maybe no sleep necessary, I think 273391: do your stuff here maybe no sleep necessary, I think 273393: do your stuff here maybe no sleep necessary, I think 273395: do your stuff here maybe no sleep necessary, I think 273397: do your stuff here maybe no sleep necessary, I think