Search code examples
bashtimerterminalbeep

bash countdown(up?) timer w/alarm: improvements?


MIN=1 && for i in $(seq $(($MIN*5)) -1 1); do echo -n "$i, "; sleep 60; done; while :; do beep -r 25 -l 3; sleep 5; done

I decided recently to experiment with the pomodoro work/learning technique and, since I am usually working with a computer, wanted to be able to cobble together a countdown/up timer with alarm that runs on the machine at which I'm working. In brief, I wanted to count down/up either a 25 minute or 5 minute interval, by minute, then have an alarm sound--an alarm that will sound until reset. I wanted this, by the way, to be a non-gui solution that I can run from a terminal

Searching the web, I came up with something that works (mostly taken from http://www.commandlinefu.com/commands/view/5886/countdown-clock )--see above. It counts down and displays time remaining by minutes and, when the specified interval ends, it plays a distinctive but fairly non-intrusive system bell pattern (uses the "beep" program) every 5 seconds until ctrl+c is entered in the terminal from which the script was run.

As I said, what I have put together seems like it will suit my needs adequately. But since my understanding of bash is quite limited--as, by looking at the script, should be evident to anyone with bash-scripting experience--I thought I would ask here what sorts of improvements might be made to this script? Can it be further condensed, and/or perhaps use different utilities to achieve the same end, but in a more elegant manner? Input will be appreciated.

LATER EDIT: Zoltan's suggestion of replacing MIN=1 && for i in $(seq $(($MIN*5)) -1 1); with for i in $(seq 5 -1 1); does, it seems to me, somewhat simplify and improve the script I posted. So a more sensible version of this as a one-liner would be for i in $(seq 5 -1 1); do echo -n "$i, "; sleep 60; done; while :; do beep -r 25 -l 3; printf " # "; sleep 5; done. The version he posted does seem a far more capable and flexible script, so I will mark it as the best answer. Although it's good to have an option like that available, I think I'll go ahead and stick with the version I created (including the slight improvement suggested by Zoltan and one I thought up that adds a hash sign each time the beep goes off [to give me an idea, in case I should leave the computer, how long it's been beeping]--the printf " # " part) that has the time interval hard-coded into the script. Thanks for the input.


Solution

  • I think Your script is fair enough.

    I don't really see the point in the && at

    MIN=1 && for
    

    it means, if the first term is successful, only then execute the second term. I am not even sure, what is the logical value of an assignment, but it is is safe to assume, it is successful. So a

    MIN=1; for ...
    

    should suffice.

    As for this 2-liner, the whole MIN=1 seems pointless, You could just write

    for i in $(seq 5 -1 1); do ...
    

    On the other hand, You might want to be able to change the alloted time, which can be achieved like this:

    1) create a file like beeper.sh

    2) make it executable by your user

    chmod u+x beeper.sh
    

    or by all user:

    chmod a+x beeper.sh
    

    3) write your script in the file, with a little tweak:

    #!/bin/bash
    
    # default timeout
    timeout_mins=5
    # while testing your script, making changes, you want this to be something like 1 or 2
    sec_per_min=60
    
    # check for command line input
    if [ 0 -ne $# ]
    then
        # assign the first command line param, no check, if it is a number, though!
        timeout_mins=$1
    fi
    
    # now it does make sense, to have a variable in the for loop core
    for i in $(seq $timeout_mins -1 1)
    do
        echo -n "$i, "
        sleep $sec_per_min
    done
    
    while :
    do
        beep -r 25 -l 3
        sleep 5
    done
    

    4) execute your script like this (from the dir the script file is in):

    # using the default time
    ./beeper.sh
    
    # using 9min
    ./beeper.sh 9
    
    # invalid input: the seq will fail -> the for will fail -> execute the beep
    ./beeper.sh bla