You are not logged in.

#1 2017-01-01 22:06:21

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

[SOLVED][BASH] Request for feedback with a simple timer script

Hello everyone,

I've been bored with bash scripting and I've decided to make a timer script, to see if 1) I could do it and 2) to further my knowledge of bash for the fun of it. Here's the current script:

[jbs@dmb-gaming-laptop timer]$ cat timer.sh 
#!/usr/bin/bash

#Simple timer script written in bash
#Define and parse our command line arguments
args=("$@")
#Bash's special SECONDS variable. Start at 0
SECONDS=0
#Reguar expression, for integer checking
regex='^[0-9]+$'
#If the argument is not a number or not exactly four digits...
if ! [[ ${args[0]} =~ $regex ]] || [[ ${#args[0]} -ne 4 ]]; then
        echo "Error: First argument is invalid... must be a 4 digit integer."
        exit 1 #Invalid argument
else
        echo "You entered: ${args[0]}, continuing..."
fi 
#The first two integers is the time in minutes
#The last two integers is the time in seconds
#For example: ./timer.sh 1230 will start the timer for
#twelve minutes and thirty seconds. Begin extracting data...
string="${args[0]}"
minutes=${string:0:2}
seconds=${string:2:4}
#Cannot have more than 60 seconds...
if [[ $seconds -gt 60 ]]; then
        echo "Error: Cannot have more than 60 seconds!"
        exit 1 #Invalid argument, because part of the arugment is bad
fi
echo "Interpreting input as $minutes minutes and $seconds seconds..."
#Convert our minutes to seconds
newMinutes=$(($minutes*60))
echo "Minutes as seconds: $newMinutes"
totalTime=$(($newMinutes + $seconds))
#Start the timer
echo "Now starting cout UP timer for a total of $totalTime seconds"
while [[ $SECONDS -le $totalTime ]]; do
    #Print only one line of output
        echo -ne "Total (seconds) --> $totalTime  Remaining (seconds) --> $SECONDS\\r"
done
echo "Time elapsed, exiting normally..."
exit 0 
[jbs@dmb-gaming-laptop timer]$ 

To use, simply call ./timer.sh <4-digit-argument>. The 4 digit argument corresponds to the minutes (first 2 digits) and seconds (last 2 digit) to run the script for. Example:

[jbs@dmb-gaming-laptop timer]$ ./timer.sh 0115
You entered: 0115, continuing...
Interpreting input as 01 minutes and 15 seconds...
Minutes as seconds: 60
Now starting cout UP timer for a total of 75 seconds
Total (seconds) --> 75  Remaining (s^Conds) --> 11
[jbs@dmb-gaming-laptop timer]$ 

Now, I'm having an annoying bug where, if you let the timer go down completely, you get an additional, erroneous line of output:

[jbs@dmb-gaming-laptop timer]$ ./timer.sh 0003
You entered: 0003, continuing...
Interpreting input as 00 minutes and 03 seconds...
Minutes as seconds: 0
Now starting cout UP timer for a total of 3 seconds
Time elapsed, exiting normally...(seconds) --> 4
[jbs@dmb-gaming-laptop timer]$ 

Notice on the third last line, the timer should have stopped. But on the line after, you see the buggy ...(seconds) --> 4 half-line. This is what I wanted feedback on. I've been stuck for a few hours already sad. Break time, and thanks for any help.

Last edited by JohnBobSmith (2017-01-05 00:10:45)


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#2 2017-01-01 22:46:41

Saint0fCloud
Member
Registered: 2009-03-31
Posts: 137

Re: [SOLVED][BASH] Request for feedback with a simple timer script

A carriage return, '\r', moves the cursor back to the beginning of the line so you're not actually clearing the previous output. You can either use terminal escapes or tput for that.

Also, one other note, why not poll using `sleep`, in keeping in line with your current approach, rather than overwriting the internal variable SECONDS?

Last edited by Saint0fCloud (2017-01-01 22:50:14)

Offline

#3 2017-01-01 23:00:23

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Sleep itself could replace this whole thing if you didn't want output.  E.g., sleep can take arbitrary time strings (Even multiples that it will add together): `sleep 2m 5s`.

But if you want to keep a running output sleep might not work well as `sleep 1` will sleep at least 1 second.  For single invocations of sleep, it's precision is quite good - but in a loop error can compound.  S0 60 `sleep 1` invocations might take notably longer than a `sleep 60` invocation (especially when other commands are being run in the loop).  So using SECONDS, or the system clock would be advisable here.

Of course, using `date -d "+ ..."` to get an end time could provide a good alternative to SECONDS.  You could even let date handle much of the string processing then.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#4 2017-01-02 00:33:00

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Thanks guys. I've combined both your thoughts into the updated script below.

[jbs@dmb-gaming-laptop timer]$ cat timer.sh 
#!/usr/bin/bash

#Simple timer script written in bash
#Define and parse our command line arguments
args=("$@")
#Reguar expression, for integer checking
regex='^[0-9]+$'
#If the argument is not a number or not exactly four digits...
if ! [[ ${args[0]} =~ $regex ]] || [[ ${#args[0]} -ne 4 ]]; then
        echo "Error: Mandatory argument is invalid... must be a 4 digit integer."
        exit 1 #Invalid argument
else
        echo "You entered: ${args[0]}, continuing..."
fi 
#The first two integers is the time in minutes
#The last two integers is the time in seconds
#For example: ./timer.sh 1230 will start the timer for
#twelve minutes and thirty seconds. Begin extracting data...
string="${args[0]}"
minutes=${string:0:2}
seconds=${string:2:4}
#Cannot have more than 60 seconds...
if [[ $seconds -gt 60 ]]; then
        echo "Error: Cannot have more than 60 seconds!"
        exit 1 #Invalid argument, because part of the arugment is bad
fi
echo "Interpreting input as $minutes minutes and $seconds seconds..."
#Convert our minutes to seconds
newMinutes=$(($minutes*60))
echo "Minutes as seconds: $newMinutes"
totalTime=$(($newMinutes + $seconds))
#The combined, total time must be greater than or equal to one (not zero)
if [[ $totalTime -eq 0 ]]; then
        echo "Error: Your time must be greater than 0 seconds! Exiting..."
        exit 1 #four 0's is a bad argument
fi
#Confirm the user wants to start the timer
echo "Start count UP timer for a total of $totalTime seconds? [y/n]"
read input
if [[ $input == "y" || $input == "Y" ]]; then
        echo "Timer started!"
        sleep 1 #Make sure we see the above echo
else
        #We will interpret giberish and, consequently, n or N, to mean NO
        echo "No requested, or bad input detected... Exiting with status 2"
        exit 2 #Exit by user or "abnormal" exit 
fi
#Bash's special SECONDS variable. Start at 0
#Init this variable only after we are guarunteed that a timer will start.
SECONDS=0
while [[ $SECONDS -le $totalTime ]]; do
        #Print only one line of output
        echo -ne "Total (seconds) --> $totalTime  Remaining (seconds) --> $SECONDS"
        #Pause for one second to avoid spamming the terminal
        sleep 1s
        #Clear the line
        tput clear
done
echo "Time elapsed, exiting normally"
exit 0
#The idea is to invoke something else here, like a sound or
#even a GUI alert when I get that far
[jbs@dmb-gaming-laptop timer]$ 

Basically, I've added more checks and interactivity. 0000 is now bad input, among other things. I've also fixed a bug where the SECONDS variable was counting up too early. Speaking of which, can we get this to count down somehow?? I really dont care either way, but a count UP timer is a bit counter-intuitive.

Trilby, I was hoping to have an interactive timer. So then I would have a dedicated workspace to my terminal timers. Say, to remind me when to go to sleep or when to take a break. I'm hoping to add some sort of GUI alert, sound, or maybe even "crash" my laptop to enforce such things upon myself a bit more. As a bit of self-discipline so I actually *do* the things I *have* to be doing in a day, if that makes sense. A sort of new-years resolution to better myself. My cellphone timers have failed me for this purpose thus far.


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#5 2017-01-02 02:15:39

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: [SOLVED][BASH] Request for feedback with a simple timer script

You may want to reduce the sleep from 1s otherwise you might skip some seconds (e.g., from 7 to 5 remaining).  Sleep can take fractions of a second, so perhaps sleep 0.1 would do.  This way the loop doesn't run too fast, but the resolution is still good.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#6 2017-01-02 02:27:02

Docbroke
Member
From: India
Registered: 2015-06-13
Posts: 1,433

Re: [SOLVED][BASH] Request for feedback with a simple timer script

One correction,

time displayed in not remaining time but it is completed seconds so

 echo -ne "Total (seconds) --> $totalTime  [b]Completed[/b] (seconds) --> $SECONDS"

I would suggest two things,
1. avoid confirmation, like "start count UP timer for total 10 seconds ?" it wastes time
2. in the last line, something like

 echo "Time elapsed, exiting normally" && mpv ~/Music/alarm.mp3

Offline

#7 2017-01-02 03:26:52

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

With both the suggestions, I've reduced the sleep time and removed user checking/input. I've also omitted all of my non-error related echos (except the exit of course). So ./timer.sh 0005 immediately starts a 5 second timer with no hiccups. I have yet to add the mpv stuff, but that's what I am going to be working on next. smile

[jbs@dmb-gaming-laptop timer]$ cat timer.sh 
#!/usr/bin/bash

#Simple timer script written in bash
#Clear the screen immediately, prevents awkward outputs
tput clear
#Define our command line argument
args=("$@")
#Reguar expression, for integer checking
regex='^[0-9]+$'
#If the argument is not a number or not exactly four digits...
if ! [[ ${args[0]} =~ $regex ]] || [[ ${#args[0]} -ne 4 ]]; then
        echo "Error: Mandatory 1st argument is invalid... must be a 4 digit integer."
        exit 1 #Invalid argument
fi
#The first two integers is the time in minutes
#The last two integers is the time in seconds
#For example: ./timer.sh 1230 will start the timer for
#twelve minutes and thirty seconds. Begin extracting data...
string="${args[0]}"
minutes=${string:0:2}
seconds=${string:2:4}
#Cannot have more than 60 seconds...
if [[ $seconds -gt 60 ]]; then
        echo "Error: Cannot have more than 60 seconds!"
        exit 1 #Invalid argument
fi
#Convert our minutes to seconds
newMinutes=$(($minutes*60))
totalTime=$(($newMinutes + $seconds))
#The combined, total time must be greater than or equal to one (not zero)
if [[ $totalTime -eq 0 ]]; then
        echo "Error: Your time must be greater than 0 seconds! Exiting..."
        exit 1 #four 0's is a bad argument
fi
#Bash's special SECONDS variable. Start at 0
#Init this variable only after we are guarunteed that a timer will start.
SECONDS=0
while [[ $SECONDS -le $totalTime ]]; do
        #Timer output. Print these lines only once each.
        echo "Timer running. Stats:"
        echo -ne "Total time, as seconds --> $totalTime |  Completed time, as seconds  --> $SECONDS"
        #Pause for one tenth of a second to avoid spamming the terminal
        sleep 0.1
        #Clear the line
        tput clear
done
echo "Timer elapsed!"
exit 0 #No error
#The idea is to invoke something else here, like a sound or
#even a GUI alert when I get that far
[jbs@dmb-gaming-laptop timer]$ 

Having output with minutes and seconds would be really nice though... I'm going to work on that too.

EDIT: Is there a way to generate audio? Invoking a speaker-test should work for now, that noise is pretty harsh. Basically, I'd like to run the script from anywhere and not have to worry about missing audio files/queues.

Last edited by JohnBobSmith (2017-01-02 03:29:59)


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#8 2017-01-02 04:19:41

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Speaker-test has a lot of options - check the man page.

Your args variable is a bit odd.  You take an array and assign it to a new array - so it's just completely redundant.  More importantly, you never use anything but $1 anyways - so you take the parameters as an array, save them to a new array, then work with the first element of the array.  Cut out all of that and just work with "$1".

The same with your variable called "string": you assign the contents of $1 to string, then split that into minutes and seconds - just split $1 into minutes and seconds.  You also don't need yet another variable for the intermediate step of the aritmetic.  I also think you could just check that the value is not zero when you check other parts of the argument.  Applying these changes, you are left with this:

#!/usr/bin/bash

tput clear
if  ! [[ $1 =~ ^[0-9]+$ ]] || [[ ${#1} -ne 4 ]] || [[ $1 -eq 0 ]]; then
        echo "Error: Mandatory 1st argument is invalid... must be a 4 digit positive integer."
        exit 1 #Invalid argument
fi
totalTime=$((${1:0:2} * 60 + ${1:2:4}))
SECONDS=0
while [[ $SECONDS -le $totalTime ]]; do
        #Timer output. Print these lines only once each.
        echo "Timer running. Stats:"
        echo -ne "Total time, as seconds --> $totalTime |  Completed time, as seconds  --> $SECONDS"
        #Pause for one tenth of a second to avoid spamming the terminal
        sleep 0.1
        #Clear the line
        tput clear
done
echo "Timer elapsed!"

"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#9 2017-01-02 04:39:10

Docbroke
Member
From: India
Registered: 2015-06-13
Posts: 1,433

Re: [SOLVED][BASH] Request for feedback with a simple timer script

few more suggestion,
1. to use "tput clear" after error message, no need to clean screen to display error message
2. changed the look of output a bit so the script for me looks like this

#!/usr/bin/bash

if  ! [[ $1 =~ ^[0-9]+$ ]] || [[ ${#1} -ne 4 ]] || [[ $1 -eq 0 ]]; then
        echo "Error: Mandatory 1st argument is invalid... must be a 4 digit positive integer."
        exit 1 #Invalid argument
fi
tput clear
totalTime=$((${1:0:2} * 60 + ${1:2:4}))
SECONDS=0
while [[ $SECONDS -le $totalTime ]]; do
        #Timer output. Print these lines only once each.
        echo "Timer running:"
	echo
        echo -ne "$SECONDS | $totalTime seconds"
        #Pause for one tenth of a second to avoid spamming the terminal
        sleep 0.1
        #Clear the line
        tput clear
done
mpv ~/Music/alarm.mp3

EDIT: few more thing
1. output is little confusing for longer time intervals like 20min is displayed as 1200 seconds, so I would suggest to convert seconds to minutes in display (reverse of what we did earlier by converting input from minutes to seconds)
2. if there are only 2 digit as input consider that as seconds

Last edited by Docbroke (2017-01-02 05:04:24)

Offline

#10 2017-01-02 14:56:50

Raynman
Member
Registered: 2011-10-22
Posts: 1,539

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Correction and a couple more suggestions

This has been cleaned up quite nicely already, but there's a tiny mistake in the substring extraction: the second number is the (maximum) length of the substring, not an index indicating the end of the range. So the seconds part should be ${1:2:2} although a length of 4 also works because there are no more characters after the seconds.

Wrapping each test in its own pair of opening/closing brackets unnecessarily clutters the if condition. And the =~ operator works with extended regular expressions, so the length check is easily incorporated into the regex:

if [[ ! $1 =~ ^[0-9]{4}$ || $1 -eq 0 ]]; then

To accept either exactly 2 or exactly 4 digits (which IMHO is still a bit rigid), you could change the regex to ^([0-9]{2}){1,2}$. You could go a little further and make both minutes and leading zeroes optional:

if [[ ! $1 =~ ^[0-9]{1,4}$ || $1 -eq 0 ]]; then
    # complain, exit
fi
# pad $1 with zeroes
set -- $(printf '%04d' $1)

Or take minutes and seconds as separate arguments (or one argument with colon as separator) and make minutes optional.

Alternative: using date to parse input (also: countdown as output)

Another way to loosen the input restrictions is to use date (as hinted at by Trilby):

#!/usr/bin/bash

totalTime=$(TZ=UTC0 date --date="1970-01-01 00:00:00 UTC $*" +'%s' 2>/dev/null)
if [[ $? -ne 0 || $totalTime -le 0 ]]; then
    echo "Invalid duration"
    exit 1
fi
echo "Timer running:"
echo
while [[ $SECONDS -le $totalTime ]]; do
        TZ=UTC0 printf '%(%M:%S)T remaining\r' $((totalTime - SECONDS))
        sleep 0.1
done
mpv ~/Music/alarm.mp3

totalTime is still in seconds. We work with time relative to the unix epoch (1970-01-01 00:00:00 UTC). With date we can convert any relative time specification to seconds. As long as date accepts it and it results in a positive number of seconds, I'm happy. We specify TZ=UTC0 because we don't want any offsets or DST jumps from the current timezone. This can be used as follows:

$ timer.sh 1min 15secs
$ timer.sh 100seconds
$ timer.sh 5 days +2 hours
$ timer.sh week - 6 days

This shows that whitespace is optional in some places and that date accepts singular/plural/abbreviated units with optionally signed multipliers (the first 1 in the first example is also optional). See `info date` for more. If you don't like having to specify units, you can compute totalTime as before, or use one method as a fallback for the other.

For the output, we could have used date again (with --date="@$((..))"), but the built-in printf also handles date formats. You can use a different format string (like %T) to accommodate timers beyond an hour or you could determine the prettiest formatting string before the loop depending on the totalTime.

Further notes:

  • There should be no need to reset $SECONDS. Unless you source this in an already running bash instance, a new shell is started when the script starts, with $SECONDS at 0. And if you do include this as part of a larger script, you shouldn't modify this global variable to make the start time 0, but instead record the value of $SECONDS before the loop as your start time.

  • I removed the screen clearing. I didn't like the flickering I saw in urxvt and with this format string each iteration prints the same number of characters. You may want to print a newline after the loop (or clear the screen before and after the loop), but if you immediately have mpv printing a bunch of stuff, that doesn't really matter.

Last edited by Raynman (2017-01-02 15:13:00)

Offline

#11 2017-01-02 18:32:42

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Wow! Thanks guys! I'm going to post my thoughts, and then edit this post when I've re-worked the script.

@Trilby I knew that there had better way of getting CLI arguments. That's a minor fail on my part. Thanks for the catch, I'll definitely use $1 from now on. Combining all the error checks is a great idea too. Awesome smile. I'll also look into speaker-test a bit more, it seems quite useful beyond its intended purpose.

@Docbroke yeah the time output in seconds only is rather awful... I'm definitely going to fix this. For your second point, are you saying that ./timer 05 should be a 5 second timer? This might be possible, but not something I'm ready to do juuussst yet.

@Raynman It's beginning to look like using DATE and it's features is definitely going to be the way to go. Especially if it gives my my count DOWN timer, hehe

I'lll post back in a bit with a much improved script. smile


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#12 2017-01-02 19:00:44

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: [SOLVED][BASH] Request for feedback with a simple timer script

JohnBobSmith wrote:

i'll definitely use $1 from now on.

It's not always the right solution.  Often you will want to use the whole array of parameters passed to a script.  But the logic of your script already expects and only processes the first parameter, so $1 is definitely best for this task.

One step more involved would be a loop like this:

while [[ -n $1 ]]; do
   # do something with parameter in $1
   shift;
done

This will process on argument at a time; each time through the loop the "shift" moves the subsequent parameter up to the first position.

Then for anything more elaborate, just use getopt(s) (see man getopt/man getopts).  These two tools are unfortunately similarly named, but they have some different functionality.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#13 2017-01-02 19:53:58

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

I see.

Here is the improved, and close-to-being-final script:

[jbs@dmb-gaming-laptop timer]$ cat timer.sh 
#!/usr/bin/bash

#Time since the epoch + time argument
totalTime=$(TZ=UTC0 date --date="1970-01-01 00:00:00 UTC $*" +'%s' 2>/dev/null)
if [[ $? -ne 0 || $totalTime -le 0 ]]; then
    echo "Invalid duration"
    exit 1
fi
echo "Timer started:"
while [[ $SECONDS -le $totalTime ]]; do
        TZ=UTC0 printf '%(%H:%M:%S)T Remaining\r' $((totalTime - SECONDS))
        sleep 0.1
done
tput clear
echo "Timer elapsed!"
#Ensure that the sound is not muted, 
#and that our volume is reasonable
amixer set Master 75% 1>/dev/null #Supress outputs
amixer set Master unmute 1>/dev/null
#Generate a sine frequency.
speaker-test -t sine --frequency 750 1>/dev/null
exit 0 #No error
[jbs@dmb-gaming-laptop timer]$ 

Essentially, using date works soooo well, It's by far the best solution. Adding secs/hours as the argument is a minor inconvenience in exchange for the removed ambiguity. Following up on speaker-test, I've manage to create a wicked awful sine noise. You have been warned! There is one minor thing that I don't understand:

'%(%H:%M:%S)T

The 'T' looks so out of place, yet it's not because I have problems when removing it. According to the man pages, %T is the same as the %H%M%S I've added. So... why does the T have to be there?

This has been a great learning experience and thanks for all the help thus far!

Last edited by JohnBobSmith (2017-01-02 19:54:24)


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#14 2017-01-02 20:21:15

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: [SOLVED][BASH] Request for feedback with a simple timer script

JohnBobSmith wrote:

According to the man pages, %T is the same as the %H%M%S I've added.

According to which man page?

man bash wrote:

printf
    %(datefmt)T
        causes printf to output the date-time string resulting from using datefmt as a format string for strftime(3)...


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#15 2017-01-02 20:22:55

Raynman
Member
Registered: 2011-10-22
Posts: 1,539

Re: [SOLVED][BASH] Request for feedback with a simple timer script

JohnBobSmith wrote:

There is one minor thing that I don't understand:

'%(%H:%M:%S)T

The 'T' looks so out of place, yet it's not because I have problems when removing it. According to the man pages, %T is the same as the %H%M%S I've added. So... why does the T have to be there?

There are two different Ts in play here. One is part of one extension added by the built-in printf provided by bash:

man bash wrote:

%(datefmt)T
    causes printf to output the date-time string resulting from using datefmt as a format string for strftime(3)

As part of the datefmt, you can use %T (this is the second one), which is supported by strftime as an abbreviation for %H:%M:%S. So your format string could be abbreviated to

'%(%T)T'

Edit: a little slow...
Edit2:

JohnBobSmith wrote:

@Raynman It's beginning to look like using DATE and it's features is definitely going to be the way to go. Especially if it gives my my count DOWN timer, hehe

Raynman wrote:

If you don't like having to specify units, you can compute totalTime as before, or use one method as a fallback for the other.

So the countdown is not really linked to the use of date for obtaining the totalTime. I just used one script to show both parts.

Last edited by Raynman (2017-01-02 20:29:40)

Offline

#16 2017-01-03 00:15:30

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

@Trilby I was looking at 'man date' in that post. H:M:S is exactly as you would expect, Hours, Minutes, Seconds.

Turns out, I should have read 'man bash' more carefully. Thanks for clarifying! I think I will keep the %H:%M:%S because it is less ambiguous and easier to read at a glance, in my opinion. I'm very close to the final script now. I'm thinking that the ability to optionally read files into mpv would be fantastic, instead of just using speaker-test. So I could then, say, ./timer.sh 30mins ~/timer/takebreak.wav. I'm thinking that the sine noise won't work for everything. As in, it'd be nice to be able to distinguish between tones/sounds. Also, setting things up in this way means I'm not hard-coding anything. So directory changes and file renames would be completely non-issues (ideally...). smile


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

#17 2017-01-03 06:03:32

Docbroke
Member
From: India
Registered: 2015-06-13
Posts: 1,433

Re: [SOLVED][BASH] Request for feedback with a simple timer script

The speaker-test sound is difficult to control, you can use it to wake-up snoring giants. I would recommand to use mpv or similar solution.

Offline

#18 2017-01-04 16:43:46

JohnBobSmith
Member
From: Canada
Registered: 2014-11-29
Posts: 804

Re: [SOLVED][BASH] Request for feedback with a simple timer script

Alright folks, here's nearing the final version of the script. Thank you to all who contributed!
What I've done is addded an optional mpv argument, make the date stuff take $1 instead of $* as the argument, and a few misc changes the code shall reveal:

[jbs@dmb-gaming-laptop timer]$ cat timer.sh 
#!/usr/bin/bash
#A simple bash timer script
#Time since the epoch + time argument
totalTime=$(TZ=UTC0 date --date="1970-01-01 00:00:00 UTC $1" +'%s' 2>/dev/null)
#If our first argument is null (zero) or the timer argument is bad
if [[ -z $1 || $totalTime -le 0 ]]; then
    echo "Error: Invalid mandatory first argument. Must be a time format accepted by date."
        echo "See 'man date' for details."
    exit 1 #Bad first argument
fi
#Optional second argument
if [[ -n $2 ]]; then #$2 Contains something, so...
        if ! [[ -f $2 ]]; then #Ensure it is a regular file
                echo "Error: Invalid optial second argument."
                echo "Must be a path which points to a valid audio file"
                exit 2 #Bad second argument
        fi
fi
#Start the timer
echo "Timer started:"
while [[ $SECONDS -le $totalTime ]]; do
        TZ=UTC0 printf '%(%H:%M:%S)T Remaining\r' $((totalTime - SECONDS))
        sleep 0.1
done
tput clear
#Ensure that the sound is not muted, 
#and that our volume is reasonable
amixer set Master 75% 1>/dev/null #Supress outputs
amixer set Master unmute 1>/dev/null
echo "Timer elapsed!"
#$2 Must contain something before invoking mpv
#It is assumed that the error checks at the top of
#The script passed if $2 was set. Otherwise,
#$2 contains nothing. Therefore, do not invoke mpv
#if $2 is blank, as this would be an error: no mandatory argument
if [[ -n $2 ]]; then
         mpv "$2" 1>/dev/null
fi
#If mpv error'ed out, inform the user,
#because it is impossible (or I don't know how) to detect
#a valid audio file and not just some random crap.txt as input
if [[ $? -ne 0 ]]; then #Fail, bad argument to mpv... Program is assumed to 
                                                #have 0-status Up until this point.
        echo "Program reached the end, but exited with an error in mpv..."
        echo "In the future, please give mpv a proper audio file."
        exit 3 #Bad arg to mpv
else
        exit 0 #No error
fi
[jbs@dmb-gaming-laptop timer]$ 

I've added a great deal of error checking with mpv. I don't want  this to outright crash without echoing why. Speaker-test has been canned, but that was a fun experience in its own right. I'm leaving for the day now. Be back ~8 hours. smile

EDIT: I'm going to mark this as solved since I have a very nice timer script now. Thanks again!

Last edited by JohnBobSmith (2017-01-05 00:09:50)


I am diagnosed with bipolar disorder. As it turns out, what I thought was my greatest weakness is now my greatest strength.

Everyday, I make a conscious choice to overcome my challenges and my problems. It's not easy, but its better than the alternative...

Offline

Board footer

Powered by FluxBB