Sleep until specified time











up vote
3
down vote

favorite












It's sometimes useful to pause until a specified time (but inconvenient to use at or the like), so I wrote a tiny script to wait until a user-specified time point is reached.



I use this when I want to keep the output of preceding and subsequent commands together, but I don't mind blocking the current terminal. Quite often, this would be M-xcompile when I want to wait until after new source code/data have been uploaded, but still have the results in a compilation-mode buffer.



Although it's a very short script, my time at Code Review has taught me that almost any program has something that can be improved, so please make your suggestions!



#!/bin/sh

# Sleep until the specified time

# Accepts any date/time format accepted by 'date'

# Assumes GNU date and GNU sleep

die() {
echo "$@" >&2
exit 1
}

usage() {
echo "Usage: $0 TIME"
}


test $# = 1 || die $(usage)

case "$1" in
--version)
echo "sleep_until version 1.0"
exit 0
;;
--help)
usage
exit 0
;;
-*)
die "Unrecognised option: $1"
;;
*)
end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"
exec sleep $(echo $end $now - p | dc )
;;
esac


Notes:




  • We need GNU date for its -d option and a good range of input formats.

  • GNU sleep handles fractional seconds; we could remove .%N from the date formats to work with traditional/POSIX sleep.

  • I've used dc for the arithmetic so we can use plain sh rather than requiring a more heavyweight shell.










share|improve this question




















  • 1




    Explain your motivation more? Why not use at?
    – 200_success
    Oct 1 at 11:35










  • I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
    – Toby Speight
    Oct 1 at 12:39















up vote
3
down vote

favorite












It's sometimes useful to pause until a specified time (but inconvenient to use at or the like), so I wrote a tiny script to wait until a user-specified time point is reached.



I use this when I want to keep the output of preceding and subsequent commands together, but I don't mind blocking the current terminal. Quite often, this would be M-xcompile when I want to wait until after new source code/data have been uploaded, but still have the results in a compilation-mode buffer.



Although it's a very short script, my time at Code Review has taught me that almost any program has something that can be improved, so please make your suggestions!



#!/bin/sh

# Sleep until the specified time

# Accepts any date/time format accepted by 'date'

# Assumes GNU date and GNU sleep

die() {
echo "$@" >&2
exit 1
}

usage() {
echo "Usage: $0 TIME"
}


test $# = 1 || die $(usage)

case "$1" in
--version)
echo "sleep_until version 1.0"
exit 0
;;
--help)
usage
exit 0
;;
-*)
die "Unrecognised option: $1"
;;
*)
end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"
exec sleep $(echo $end $now - p | dc )
;;
esac


Notes:




  • We need GNU date for its -d option and a good range of input formats.

  • GNU sleep handles fractional seconds; we could remove .%N from the date formats to work with traditional/POSIX sleep.

  • I've used dc for the arithmetic so we can use plain sh rather than requiring a more heavyweight shell.










share|improve this question




















  • 1




    Explain your motivation more? Why not use at?
    – 200_success
    Oct 1 at 11:35










  • I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
    – Toby Speight
    Oct 1 at 12:39













up vote
3
down vote

favorite









up vote
3
down vote

favorite











It's sometimes useful to pause until a specified time (but inconvenient to use at or the like), so I wrote a tiny script to wait until a user-specified time point is reached.



I use this when I want to keep the output of preceding and subsequent commands together, but I don't mind blocking the current terminal. Quite often, this would be M-xcompile when I want to wait until after new source code/data have been uploaded, but still have the results in a compilation-mode buffer.



Although it's a very short script, my time at Code Review has taught me that almost any program has something that can be improved, so please make your suggestions!



#!/bin/sh

# Sleep until the specified time

# Accepts any date/time format accepted by 'date'

# Assumes GNU date and GNU sleep

die() {
echo "$@" >&2
exit 1
}

usage() {
echo "Usage: $0 TIME"
}


test $# = 1 || die $(usage)

case "$1" in
--version)
echo "sleep_until version 1.0"
exit 0
;;
--help)
usage
exit 0
;;
-*)
die "Unrecognised option: $1"
;;
*)
end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"
exec sleep $(echo $end $now - p | dc )
;;
esac


Notes:




  • We need GNU date for its -d option and a good range of input formats.

  • GNU sleep handles fractional seconds; we could remove .%N from the date formats to work with traditional/POSIX sleep.

  • I've used dc for the arithmetic so we can use plain sh rather than requiring a more heavyweight shell.










share|improve this question















It's sometimes useful to pause until a specified time (but inconvenient to use at or the like), so I wrote a tiny script to wait until a user-specified time point is reached.



I use this when I want to keep the output of preceding and subsequent commands together, but I don't mind blocking the current terminal. Quite often, this would be M-xcompile when I want to wait until after new source code/data have been uploaded, but still have the results in a compilation-mode buffer.



Although it's a very short script, my time at Code Review has taught me that almost any program has something that can be improved, so please make your suggestions!



#!/bin/sh

# Sleep until the specified time

# Accepts any date/time format accepted by 'date'

# Assumes GNU date and GNU sleep

die() {
echo "$@" >&2
exit 1
}

usage() {
echo "Usage: $0 TIME"
}


test $# = 1 || die $(usage)

case "$1" in
--version)
echo "sleep_until version 1.0"
exit 0
;;
--help)
usage
exit 0
;;
-*)
die "Unrecognised option: $1"
;;
*)
end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"
exec sleep $(echo $end $now - p | dc )
;;
esac


Notes:




  • We need GNU date for its -d option and a good range of input formats.

  • GNU sleep handles fractional seconds; we could remove .%N from the date formats to work with traditional/POSIX sleep.

  • I've used dc for the arithmetic so we can use plain sh rather than requiring a more heavyweight shell.







datetime timer shell scheduled-tasks






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Oct 1 at 12:41

























asked Oct 1 at 8:51









Toby Speight

22.4k537109




22.4k537109








  • 1




    Explain your motivation more? Why not use at?
    – 200_success
    Oct 1 at 11:35










  • I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
    – Toby Speight
    Oct 1 at 12:39














  • 1




    Explain your motivation more? Why not use at?
    – 200_success
    Oct 1 at 11:35










  • I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
    – Toby Speight
    Oct 1 at 12:39








1




1




Explain your motivation more? Why not use at?
– 200_success
Oct 1 at 11:35




Explain your motivation more? Why not use at?
– 200_success
Oct 1 at 11:35












I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
– Toby Speight
Oct 1 at 12:39




I've added a sentence with more motivation. Really, it's just that I was already using sleep and getting tired of doing datetime subtractions in my head.
– Toby Speight
Oct 1 at 12:39










2 Answers
2






active

oldest

votes

















up vote
2
down vote



accepted










In a program comment it is said that the program



# Accepts any date/time format accepted by 'date'


That information should be printed with the usage help, plus one or two examples. Something like




$ ./sleep_until.sh --help
Usage: ./sleep_until.sh TIME

TIME can be any date/time as accepted by date(1).

Examples:

...


That'll save me from looking into the source code (and possibly from
looking up man date) in order to figure out the correct parameters.





Due to the truncation in



test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"


there is a (albeit small) chance that a valid date is not recognized.
This can be avoided by computing the delay first, and then checking
it to be positive (e.g. as in How to compare to floating point number in a shell script):



end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
delay=$(echo $end - $now | bc)
test 1 -eq $(echo "$delay > 0" | bc) || die "$1 is in the past!"
exec sleep $delay





share|improve this answer























  • I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
    – Toby Speight
    Oct 1 at 12:44










  • Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
    – Toby Speight
    Oct 1 at 15:53






  • 2




    @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
    – Martin R
    Oct 1 at 16:27


















up vote
1
down vote













The substitution in the final exec line can be simplified. Instead of making a pipeline with echo, we can provide input to dc as a command argument:



        exec sleep $(dc -e "$end $now -p")




I always recommend setting -e and -u options for shell scripts. So add at the beginning:



set -eu


This is particularly important, to ensure that we fail neatly when the argument is not understood by date.



I think I missed this because I thought that these were Bashisms, but they are actually specified in POSIX.





Consider adding a --verbose option to print the sleep duration (in seconds) and target time - this could be valuable with some of the relative times supported by date. The argument parsing and checking would need to be reworked, as the simple $# = 1 check would no longer be adequate.





Improved code



Applying these suggestions and those from the other answer, I have:



#!/bin/sh

# Assumes GNU date and GNU sleep

set -eu

die() {
echo "$@" >&2
exit 1
}

usage() {
cat <<EOF
Usage: $0 [-v|--verbose] TIME
or $0 --version

Sleep until the specified time.

TIME can be any date/time as accepted by date(1).

Examples:
$0 3:14
$0 'next Tuesday'
EOF
}

verbose=false

while [ "${1+y}" ]
do
case "$1" in
--version)
echo "sleep_until version 1.1"
exit 0
;;
-h|--help)
usage
exit 0
;;
-v|--verbose)
verbose=true
shift
;;
-*)
die "Unrecognised option: $1"
;;
*)
test ! "${2+y}" || die "Extra arguments after time"
end=$(date -d "$1" +%s.%N)
now=$(date +%s.%N)
duration=$(dc -e "$end $now -p")
case "$duration" in
-*) die "$1 is in the past!";;
esac
if $verbose
then
printf 'Sleeping for %g seconds until ' $duration
date -d "$1"
fi
exec sleep $duration
;;
esac
done

# If we reach here, we didn't get any non-option argument
die "No time specified"





share|improve this answer























    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204673%2fsleep-until-specified-time%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote



    accepted










    In a program comment it is said that the program



    # Accepts any date/time format accepted by 'date'


    That information should be printed with the usage help, plus one or two examples. Something like




    $ ./sleep_until.sh --help
    Usage: ./sleep_until.sh TIME

    TIME can be any date/time as accepted by date(1).

    Examples:

    ...


    That'll save me from looking into the source code (and possibly from
    looking up man date) in order to figure out the correct parameters.





    Due to the truncation in



    test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"


    there is a (albeit small) chance that a valid date is not recognized.
    This can be avoided by computing the delay first, and then checking
    it to be positive (e.g. as in How to compare to floating point number in a shell script):



    end=$(date -d "$1" +%s.%N)
    now=$(date +%s.%N)
    delay=$(echo $end - $now | bc)
    test 1 -eq $(echo "$delay > 0" | bc) || die "$1 is in the past!"
    exec sleep $delay





    share|improve this answer























    • I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
      – Toby Speight
      Oct 1 at 12:44










    • Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
      – Toby Speight
      Oct 1 at 15:53






    • 2




      @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
      – Martin R
      Oct 1 at 16:27















    up vote
    2
    down vote



    accepted










    In a program comment it is said that the program



    # Accepts any date/time format accepted by 'date'


    That information should be printed with the usage help, plus one or two examples. Something like




    $ ./sleep_until.sh --help
    Usage: ./sleep_until.sh TIME

    TIME can be any date/time as accepted by date(1).

    Examples:

    ...


    That'll save me from looking into the source code (and possibly from
    looking up man date) in order to figure out the correct parameters.





    Due to the truncation in



    test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"


    there is a (albeit small) chance that a valid date is not recognized.
    This can be avoided by computing the delay first, and then checking
    it to be positive (e.g. as in How to compare to floating point number in a shell script):



    end=$(date -d "$1" +%s.%N)
    now=$(date +%s.%N)
    delay=$(echo $end - $now | bc)
    test 1 -eq $(echo "$delay > 0" | bc) || die "$1 is in the past!"
    exec sleep $delay





    share|improve this answer























    • I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
      – Toby Speight
      Oct 1 at 12:44










    • Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
      – Toby Speight
      Oct 1 at 15:53






    • 2




      @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
      – Martin R
      Oct 1 at 16:27













    up vote
    2
    down vote



    accepted







    up vote
    2
    down vote



    accepted






    In a program comment it is said that the program



    # Accepts any date/time format accepted by 'date'


    That information should be printed with the usage help, plus one or two examples. Something like




    $ ./sleep_until.sh --help
    Usage: ./sleep_until.sh TIME

    TIME can be any date/time as accepted by date(1).

    Examples:

    ...


    That'll save me from looking into the source code (and possibly from
    looking up man date) in order to figure out the correct parameters.





    Due to the truncation in



    test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"


    there is a (albeit small) chance that a valid date is not recognized.
    This can be avoided by computing the delay first, and then checking
    it to be positive (e.g. as in How to compare to floating point number in a shell script):



    end=$(date -d "$1" +%s.%N)
    now=$(date +%s.%N)
    delay=$(echo $end - $now | bc)
    test 1 -eq $(echo "$delay > 0" | bc) || die "$1 is in the past!"
    exec sleep $delay





    share|improve this answer














    In a program comment it is said that the program



    # Accepts any date/time format accepted by 'date'


    That information should be printed with the usage help, plus one or two examples. Something like




    $ ./sleep_until.sh --help
    Usage: ./sleep_until.sh TIME

    TIME can be any date/time as accepted by date(1).

    Examples:

    ...


    That'll save me from looking into the source code (and possibly from
    looking up man date) in order to figure out the correct parameters.





    Due to the truncation in



    test ${end%.*} -gt ${now%.*} || die "$1 is in the past!"


    there is a (albeit small) chance that a valid date is not recognized.
    This can be avoided by computing the delay first, and then checking
    it to be positive (e.g. as in How to compare to floating point number in a shell script):



    end=$(date -d "$1" +%s.%N)
    now=$(date +%s.%N)
    delay=$(echo $end - $now | bc)
    test 1 -eq $(echo "$delay > 0" | bc) || die "$1 is in the past!"
    exec sleep $delay






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Oct 1 at 13:18

























    answered Oct 1 at 11:31









    Martin R

    15.3k12264




    15.3k12264












    • I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
      – Toby Speight
      Oct 1 at 12:44










    • Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
      – Toby Speight
      Oct 1 at 15:53






    • 2




      @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
      – Martin R
      Oct 1 at 16:27


















    • I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
      – Toby Speight
      Oct 1 at 12:44










    • Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
      – Toby Speight
      Oct 1 at 15:53






    • 2




      @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
      – Martin R
      Oct 1 at 16:27
















    I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
    – Toby Speight
    Oct 1 at 12:44




    I just spotted another issue with help - I really ought to recognise just -h as a synonym for --help.
    – Toby Speight
    Oct 1 at 12:44












    Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
    – Toby Speight
    Oct 1 at 15:53




    Is your use of bc (rather than dc) just a personal preference, or is it objectively better in some way, such as speed or size?
    – Toby Speight
    Oct 1 at 15:53




    2




    2




    @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
    – Martin R
    Oct 1 at 16:27




    @TobySpeight: I needed it here for the check $delay > 0 and found a bc-based solution first (and then I did not want to use two different tools). Also I am a little bit more familiar with bc. – Some criteria are given here unix.stackexchange.com/q/124518, such as “bc is standardised by POSIX and so is probably the more portable of the two”
    – Martin R
    Oct 1 at 16:27












    up vote
    1
    down vote













    The substitution in the final exec line can be simplified. Instead of making a pipeline with echo, we can provide input to dc as a command argument:



            exec sleep $(dc -e "$end $now -p")




    I always recommend setting -e and -u options for shell scripts. So add at the beginning:



    set -eu


    This is particularly important, to ensure that we fail neatly when the argument is not understood by date.



    I think I missed this because I thought that these were Bashisms, but they are actually specified in POSIX.





    Consider adding a --verbose option to print the sleep duration (in seconds) and target time - this could be valuable with some of the relative times supported by date. The argument parsing and checking would need to be reworked, as the simple $# = 1 check would no longer be adequate.





    Improved code



    Applying these suggestions and those from the other answer, I have:



    #!/bin/sh

    # Assumes GNU date and GNU sleep

    set -eu

    die() {
    echo "$@" >&2
    exit 1
    }

    usage() {
    cat <<EOF
    Usage: $0 [-v|--verbose] TIME
    or $0 --version

    Sleep until the specified time.

    TIME can be any date/time as accepted by date(1).

    Examples:
    $0 3:14
    $0 'next Tuesday'
    EOF
    }

    verbose=false

    while [ "${1+y}" ]
    do
    case "$1" in
    --version)
    echo "sleep_until version 1.1"
    exit 0
    ;;
    -h|--help)
    usage
    exit 0
    ;;
    -v|--verbose)
    verbose=true
    shift
    ;;
    -*)
    die "Unrecognised option: $1"
    ;;
    *)
    test ! "${2+y}" || die "Extra arguments after time"
    end=$(date -d "$1" +%s.%N)
    now=$(date +%s.%N)
    duration=$(dc -e "$end $now -p")
    case "$duration" in
    -*) die "$1 is in the past!";;
    esac
    if $verbose
    then
    printf 'Sleeping for %g seconds until ' $duration
    date -d "$1"
    fi
    exec sleep $duration
    ;;
    esac
    done

    # If we reach here, we didn't get any non-option argument
    die "No time specified"





    share|improve this answer



























      up vote
      1
      down vote













      The substitution in the final exec line can be simplified. Instead of making a pipeline with echo, we can provide input to dc as a command argument:



              exec sleep $(dc -e "$end $now -p")




      I always recommend setting -e and -u options for shell scripts. So add at the beginning:



      set -eu


      This is particularly important, to ensure that we fail neatly when the argument is not understood by date.



      I think I missed this because I thought that these were Bashisms, but they are actually specified in POSIX.





      Consider adding a --verbose option to print the sleep duration (in seconds) and target time - this could be valuable with some of the relative times supported by date. The argument parsing and checking would need to be reworked, as the simple $# = 1 check would no longer be adequate.





      Improved code



      Applying these suggestions and those from the other answer, I have:



      #!/bin/sh

      # Assumes GNU date and GNU sleep

      set -eu

      die() {
      echo "$@" >&2
      exit 1
      }

      usage() {
      cat <<EOF
      Usage: $0 [-v|--verbose] TIME
      or $0 --version

      Sleep until the specified time.

      TIME can be any date/time as accepted by date(1).

      Examples:
      $0 3:14
      $0 'next Tuesday'
      EOF
      }

      verbose=false

      while [ "${1+y}" ]
      do
      case "$1" in
      --version)
      echo "sleep_until version 1.1"
      exit 0
      ;;
      -h|--help)
      usage
      exit 0
      ;;
      -v|--verbose)
      verbose=true
      shift
      ;;
      -*)
      die "Unrecognised option: $1"
      ;;
      *)
      test ! "${2+y}" || die "Extra arguments after time"
      end=$(date -d "$1" +%s.%N)
      now=$(date +%s.%N)
      duration=$(dc -e "$end $now -p")
      case "$duration" in
      -*) die "$1 is in the past!";;
      esac
      if $verbose
      then
      printf 'Sleeping for %g seconds until ' $duration
      date -d "$1"
      fi
      exec sleep $duration
      ;;
      esac
      done

      # If we reach here, we didn't get any non-option argument
      die "No time specified"





      share|improve this answer

























        up vote
        1
        down vote










        up vote
        1
        down vote









        The substitution in the final exec line can be simplified. Instead of making a pipeline with echo, we can provide input to dc as a command argument:



                exec sleep $(dc -e "$end $now -p")




        I always recommend setting -e and -u options for shell scripts. So add at the beginning:



        set -eu


        This is particularly important, to ensure that we fail neatly when the argument is not understood by date.



        I think I missed this because I thought that these were Bashisms, but they are actually specified in POSIX.





        Consider adding a --verbose option to print the sleep duration (in seconds) and target time - this could be valuable with some of the relative times supported by date. The argument parsing and checking would need to be reworked, as the simple $# = 1 check would no longer be adequate.





        Improved code



        Applying these suggestions and those from the other answer, I have:



        #!/bin/sh

        # Assumes GNU date and GNU sleep

        set -eu

        die() {
        echo "$@" >&2
        exit 1
        }

        usage() {
        cat <<EOF
        Usage: $0 [-v|--verbose] TIME
        or $0 --version

        Sleep until the specified time.

        TIME can be any date/time as accepted by date(1).

        Examples:
        $0 3:14
        $0 'next Tuesday'
        EOF
        }

        verbose=false

        while [ "${1+y}" ]
        do
        case "$1" in
        --version)
        echo "sleep_until version 1.1"
        exit 0
        ;;
        -h|--help)
        usage
        exit 0
        ;;
        -v|--verbose)
        verbose=true
        shift
        ;;
        -*)
        die "Unrecognised option: $1"
        ;;
        *)
        test ! "${2+y}" || die "Extra arguments after time"
        end=$(date -d "$1" +%s.%N)
        now=$(date +%s.%N)
        duration=$(dc -e "$end $now -p")
        case "$duration" in
        -*) die "$1 is in the past!";;
        esac
        if $verbose
        then
        printf 'Sleeping for %g seconds until ' $duration
        date -d "$1"
        fi
        exec sleep $duration
        ;;
        esac
        done

        # If we reach here, we didn't get any non-option argument
        die "No time specified"





        share|improve this answer














        The substitution in the final exec line can be simplified. Instead of making a pipeline with echo, we can provide input to dc as a command argument:



                exec sleep $(dc -e "$end $now -p")




        I always recommend setting -e and -u options for shell scripts. So add at the beginning:



        set -eu


        This is particularly important, to ensure that we fail neatly when the argument is not understood by date.



        I think I missed this because I thought that these were Bashisms, but they are actually specified in POSIX.





        Consider adding a --verbose option to print the sleep duration (in seconds) and target time - this could be valuable with some of the relative times supported by date. The argument parsing and checking would need to be reworked, as the simple $# = 1 check would no longer be adequate.





        Improved code



        Applying these suggestions and those from the other answer, I have:



        #!/bin/sh

        # Assumes GNU date and GNU sleep

        set -eu

        die() {
        echo "$@" >&2
        exit 1
        }

        usage() {
        cat <<EOF
        Usage: $0 [-v|--verbose] TIME
        or $0 --version

        Sleep until the specified time.

        TIME can be any date/time as accepted by date(1).

        Examples:
        $0 3:14
        $0 'next Tuesday'
        EOF
        }

        verbose=false

        while [ "${1+y}" ]
        do
        case "$1" in
        --version)
        echo "sleep_until version 1.1"
        exit 0
        ;;
        -h|--help)
        usage
        exit 0
        ;;
        -v|--verbose)
        verbose=true
        shift
        ;;
        -*)
        die "Unrecognised option: $1"
        ;;
        *)
        test ! "${2+y}" || die "Extra arguments after time"
        end=$(date -d "$1" +%s.%N)
        now=$(date +%s.%N)
        duration=$(dc -e "$end $now -p")
        case "$duration" in
        -*) die "$1 is in the past!";;
        esac
        if $verbose
        then
        printf 'Sleeping for %g seconds until ' $duration
        date -d "$1"
        fi
        exec sleep $duration
        ;;
        esac
        done

        # If we reach here, we didn't get any non-option argument
        die "No time specified"






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 2 days ago

























        answered 2 days ago









        Toby Speight

        22.4k537109




        22.4k537109






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204673%2fsleep-until-specified-time%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Quarter-circle Tiles

            build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

            Mont Emei