1

I have a simple code to ensure a script takes at least x seconds (500 here) on Ubuntu

t1=$(date +%s)
# script is here
t2=$(date +%s)
let "t = 500 - $t2 + $t1"
(( t = t>0 ? t : 1 ))
sleep $t

The code works perfectly, but I believe my coding is not efficient, and these three lines

t2=$(date +%s)
let "t = 500 - $t2 + $t1"
(( t = t>0 ? t : 1 ))

should be expressed in one single line. My question is how to improve the code.

5
  • Is this a Linux platform, or some UNIX? (More specifically, do you have GNU date?) Commented Feb 4, 2019 at 21:48
  • 2
    There is much to be said for not sacrificing readability for perceived efficiency. If you compress that all into an inscrutable one-liner running as a subshell expansion as a parameter for sleep, how are you going to unravel that when someone calls you at 4 in the morning the day after a most raucous party to debug it when it goes sideways? Commented Feb 4, 2019 at 21:53
  • 1
    You could lose the t and t2 entirely, but make sure your phone is switched off at 4am. sleep $( printf "%d\n" $(( 500 - ($(date +%s) - t1) )) | sed 's/^-.*/1/' ) Commented Feb 4, 2019 at 22:01
  • 1
    @roaima Sorry for being vague. I updated the question. I haven't got stuck anywhere. I just want to improve my code, as I believe it is not the efficient way. Commented Feb 4, 2019 at 22:01
  • @DopeGhoti very good point indeed. Commented Feb 4, 2019 at 22:02

3 Answers 3

4

What your script is, is non-portable (since you're using let and (( .. ))), confusing (since you're using both let and (( .. ))), lacking in documentation (there are no comments, and the variable names are non-descriptive), and marginally unsafe (since you haven't quoted the expansion of $t).

If you want a rewrite, here's mine:

#!/bin/sh

min_duration=500
t_start=$(date +%s)
# script is here
t_end=$(date +%s)
elapsed=$(( t_end - t_start ))

# sleep long enough to make sure 'min_duration' seconds has elapsed, 
# but at least 1 second
sleep "$(( elapsed < min_duration ? min_duration - elapsed : 1 ))"
4
  • 1
    Is there any practical difference between your : "$(( elapsed = t_end - t_start ))" and simply (( elapsed = t_end - t_start ))? Commented Feb 4, 2019 at 22:11
  • 1
    @roaima The exit status when the calculation results in a zero. It matters when running under set -e. Personally, I think I would have used elapsed=$(( ... )). Also notice that he's writing for /bin/sh, not bash. Commented Feb 4, 2019 at 22:16
  • @roaima, I was just thinking about the portability point. At least dash and busybox don't support (( .. )). With Bash, I'd use (( .. )) since it looks less ugly to me. The exit status shouldn't matter (well, unless someone uses set -e, but let's not go there...) Commented Feb 4, 2019 at 22:18
  • @Kusalananda, argh, of course with elapsed=$((..))... Thanks. Commented Feb 4, 2019 at 22:19
2

Here is how I would do it using bash special parameter SECONDS:

#!/bin/bash                                                                                                                                                                                  

SECONDS=0
# script is here                                                                                                                                                                             
sleep "$(( 500 > SECONDS ? 500 - SECONDS : 1 ))"

Normally SECONDS returns time (in seconds) since the script has started, but one can assign any value to (re)set the timer.

1
  • 1
    Zeroing SECONDS doesn't hurt, and at least makes the initial value explicit, but since it tells the time since the script started you don't have to zero it. Unless the script does something else first, of course. Commented Feb 4, 2019 at 22:25
0

A simple alternative is to just run sleep in in the background and then wait for it after running the other code.

  #!/bin/bash

  sleep 500 &
  # script is here
  wait

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.