# Display runnning countdown in a bash script?

**URL:** <https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648>\
**Category:** Shell Programming and Scripting\
**Created:** [January 29, 2009, 10:30pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648 "2009-01-29T22:30:17Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Starcast](https://community.unix.com/user_avatar/community.unix.com/starcast/32/4786_2.png) [@Starcast](https://community.unix.com/u/Starcast)\
**Post date:** [January 29, 2009, 10:30pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/1 "2009-01-29T22:30:17Z")

</div>

I am looking for a way to display on a single line, a running countdown for a given amount of time in a terminal using a bash script.

I am looking for this to use as part of a larger bash script that captures Video. The script sets up a bunch of parameters for DVgrab, and one of the parameters set is the tape length. I would like to use this $tape\_length as the input for a running countdown so that at any time during capture I can see how much longer the capture will run for.

On a side note, in my script $tape\_length is entered in the form hh:mm:ss

I feel like this should be easy, but despite some extensive searching I can't find any leads or even scripts to steal and modify! Any help is much appreciated, I'm very new to bash scripting...

---

<div class="post-metadata">

**Author:** ![Corona688](https://community.unix.com/letter_avatar/corona688/32/6_22734026db12803ebb3e059622ac3534.png) [@Corona688](https://community.unix.com/u/Corona688)\
**Post date:** [January 29, 2009, 11:00pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/2 "2009-01-29T23:00:22Z")

</div>

This uses a few tricks to do what you want:

- The printf command does NOT append a linefeed automatically unless you tell it to.
- Setting IFS to something else lets the shell split things apart into arrays on whatever delimiter you want, in this case , :.
- The date command can be used to produce time in seconds.
- Carriage returns return the cursor to the beginning of the line without moving to the next line.

```nohighlight
function countdown
{
        local OLD_IFS="${IFS}"
        IFS=":"
        local ARR=( $1 )
        local SECONDS=$(( (ARR[0] * 60 * 60) + (ARR[1] * 60) + ARR[2] ))
        local START=$(date +%s)
        local END=$((START + SECONDS))
        local CUR=$START

        while [[$CUR -lt $END]]
        do
                CUR=$(date +%s)
                LEFT=$((END-CUR))

                printf "\r%02d:%02d:%02d" \
                        $((LEFT/3600)) $(( (LEFT/60)%60)) $((LEFT%60))

                sleep 1
        done
        IFS="${OLD_IFS}"
        echo " "
}

countdown "00:07:55"

```

---

<div class="post-metadata">

**Author:** ![Starcast](https://community.unix.com/user_avatar/community.unix.com/starcast/32/4786_2.png) [@Starcast](https://community.unix.com/u/Starcast)\
**Post date:** [January 29, 2009, 11:59pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/3 "2009-01-29T23:59:00Z")

</div>

[LEFT]That is exactly what I needed Thanks! At first it was counting down and then starting the capture, but I just added an "&" and she works great!

Timer

(Just wanted to include this for search purposes...)  
[/LEFT]

---

<div class="post-metadata">

**Author:** ![pvdb](https://community.unix.com/user_avatar/community.unix.com/pvdb/32/1399_2.png) [@pvdb](https://community.unix.com/u/pvdb)\
**Post date:** [March 30, 2009, 6:54am UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/4 "2009-03-30T06:54:16Z")

</div>

> [@corona688](#):
>
> This uses a few tricks to do what you want:

Great little script, Corona688, just what I needed! :b:

Here's a little variant I wrote that allows you to prefix the countdown timer with a custom message:

```plaintext
countdown "00:07:55" Time left before entering the matrix

```

... which produces something like:

```plaintext
Time left before entering the Matrix > 00:00:08

```

Here's my patch:

```plaintext
pvdb@localhost ~ $ diff original prefix 
5c5,7
< local ARR=( $1 )
---
> local ARR=( $1 ) ; shift
> IFS="${OLD_IFS}"
> local PREFIX="$*" ; [-n "${PREFIX}"] && PREFIX="${PREFIX} > "
16c18
< printf "\r%02d:%02d:%02d" \
---
> printf "\r${PREFIX}%02d:%02d:%02d" \
21d22
< IFS="${OLD_IFS}"
pvdb@localhost ~ $ _

```

---

<div class="post-metadata">

**Author:** ![Corona688](https://community.unix.com/letter_avatar/corona688/32/6_22734026db12803ebb3e059622ac3534.png) [@Corona688](https://community.unix.com/u/Corona688)\
**Post date:** [March 30, 2009, 11:54am UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/5 "2009-03-30T11:54:39Z")

</div>

Great idea, thanks for the patch! But since your copy/paste of it wasn't identical down to the last bit of whitespace and newline(possible disagreement on spaces per tab, etc) I'm having trouble applying it... diff -U is generally preferred these days as it gives more complete information on what's being replaced where, but a full code-listing really can't be that much larger.

But I think I see a potential problem in your use of printf: if your PREFIX variable includes control sequences like %d it will cause printf to do strange things since it will insert parameters from the commandline. Try inserting %s instead of PREFIX, and making "${PREFIX}" the first argument printf is given. This will guarantee the string is printed as 100% literals.

---

<div class="post-metadata">

**Author:** ![cfajohnson](https://community.unix.com/user_avatar/community.unix.com/cfajohnson/32/16669_2.png) [@cfajohnson](https://community.unix.com/u/cfajohnson)\
**Post date:** [March 30, 2009, 5:39pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/6 "2009-03-30T17:39:11Z")

</div>

> [@corona688](#):
>
> ```plaintext
> countdown "00:07:55"
> 
> ```

That will fail for 00:08:09.

Here is my version, which will work in any POSIX shell:

```plaintext
countdown()
(
  IFS=:
  set -- $*
  secs=$(( ${1#0} * 3600 + ${2#0} * 60 + ${3#0} ))
  while [$secs -gt 0]
  do
    sleep 1 &
    printf "\r%02d:%02d:%02d" $((secs/3600)) $(( (secs/60)%60)) $((secs%60))
    secs=$(( $secs - 1 ))
    wait
  done
  echo
)

```

---

<div class="post-metadata">

**Author:** ![pvdb](https://community.unix.com/user_avatar/community.unix.com/pvdb/32/1399_2.png) [@pvdb](https://community.unix.com/u/pvdb)\
**Post date:** [April 2, 2009, 3:53am UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/7 "2009-04-02T03:53:23Z")

</div>

Hi cfajohnson,

Thanks for your version of the script... very enlightening!

Quick question: instead of doing a "raw" sleep...

```nohighlight
sleep 1

```

your version does this instead...

```nohighlight
    sleep 1 &
    ...
    wait

```

I'm assuming doing it does way minimizes the "skew" on the actual time spent in the function, ie. every cycle is as close to 1 second as possible, as opposed to 1 second + however long it takes to do all the work.

Is that correct, or is there another benefit in doing it this way?

Regards,

PVDB

---

<div class="post-metadata">

**Author:** ![cfajohnson](https://community.unix.com/user_avatar/community.unix.com/cfajohnson/32/16669_2.png) [@cfajohnson](https://community.unix.com/u/cfajohnson)\
**Post date:** [April 2, 2009, 4:13am UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/8 "2009-04-02T04:13:17Z")

</div>

> [@pvdb](#):
>
> I'm assuming doing it does way minimizes the "skew" on the actual time spent in the function, ie. every cycle is as close to 1 second as possible, as opposed to 1 second + however long it takes to do all the work.
> 
> Is that correct, or is there another benefit in doing it this way?

That's exactly right.

---

<div class="post-metadata">

**Author:** ![pvdb](https://community.unix.com/user_avatar/community.unix.com/pvdb/32/1399_2.png) [@pvdb](https://community.unix.com/u/pvdb)\
**Post date:** [April 2, 2009, 5:22pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/9 "2009-04-02T17:22:14Z")

</div>

Hi Corona688,

> [@corona688](#):
>
> Great idea, thanks for the patch!

All good points you raise... here's a unified diff of the two scripts, which also addresses the issue you correctly point out with printf control characters in the prefix:

```plaintext
pvdb@localhost ~$ diff -U 3 original prefix 
--- original	2009-04-02 22:08:47.000000000 +0100
+++ prefix	2009-04-02 22:13:06.000000000 +0100
@@ -2,7 +2,9 @@
 {
         local OLD_IFS="${IFS}"
         IFS=":"
- local ARR=( $1 )
+ local ARR=( $1 ) ; shift
+ IFS="${OLD_IFS}"
+ local PREFIX="$*" ; [-n "${PREFIX}"] && PREFIX="${PREFIX} > "
         local SECONDS=$(( (ARR[0] * 60 * 60) + (ARR[1] * 60) + ARR[2] ))
         local START=$(date +%s)
         local END=$((START + SECONDS))
@@ -13,12 +15,11 @@
                 CUR=$(date +%s)
                 LEFT=$((END-CUR))
 
- printf "\r%02d:%02d:%02d" \
- $((LEFT/3600)) $(( (LEFT/60)%60)) $((LEFT%60))
+ printf "\r%s%02d:%02d:%02d" \
+ "${PREFIX}" $((LEFT/3600)) $(( (LEFT/60)%60)) $((LEFT%60))
 
                 sleep 1
         done
- IFS="${OLD_IFS}"
         echo " "
 }
 
pvdb@localhost ~/Desktop $ _

```

... as well as the patched script in its entirity:

```plaintext
function countdown
{
        local OLD_IFS="${IFS}"
        IFS=":"
        local ARR=( $1 ) ; shift
        IFS="${OLD_IFS}"
        local PREFIX="$*" ; [-n "${PREFIX}"] && PREFIX="${PREFIX} > "
        local SECONDS=$(( (ARR[0] * 60 * 60) + (ARR[1] * 60) + ARR[2] ))
        local START=$(date +%s)
        local END=$((START + SECONDS))
        local CUR=$START

        while [[$CUR -lt $END]]
        do
                CUR=$(date +%s)
                LEFT=$((END-CUR))

                printf "\r%s%02d:%02d:%02d" \
                        "${PREFIX}" $((LEFT/3600)) $(( (LEFT/60)%60)) $((LEFT%60))

                sleep 1
        done
        echo " "
}

```

Regards,

PVDB

---

<div class="post-metadata">

**Author:** ![cfajohnson](https://community.unix.com/user_avatar/community.unix.com/cfajohnson/32/16669_2.png) [@cfajohnson](https://community.unix.com/u/cfajohnson)\
**Post date:** [April 2, 2009, 5:38pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/10 "2009-04-02T17:38:43Z")

</div>

That still fails if the arg includes 08 or 09.

---

<div class="post-metadata">

**Author:** ![pvdb](https://community.unix.com/user_avatar/community.unix.com/pvdb/32/1399_2.png) [@pvdb](https://community.unix.com/u/pvdb)\
**Post date:** [April 2, 2009, 5:49pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/11 "2009-04-02T17:49:23Z")

</div>

Hi cfajohnson,

> [@cfajohnson](#):
>
> That still fails if the arg includes 08 or 09.

Yes, absolutely agree, and your version of the script is definitely the way to go, but by posting my corrected patch, I was just finishing what I started, so to speak 🙂

---

<div class="post-metadata">

**Author:** ![cfajohnson](https://community.unix.com/user_avatar/community.unix.com/cfajohnson/32/16669_2.png) [@cfajohnson](https://community.unix.com/u/cfajohnson)\
**Post date:** [April 2, 2009, 6:20pm UTC](https://community.unix.com/t/display-runnning-countdown-in-a-bash-script/229648/12 "2009-04-02T18:20:21Z")

</div>

I should have added that yours is easily fixed:

```nohighlight
local SECONDS=$(( ${ARR[0]#0} * 3600 + ${ARR[1]#0} * 60 + ${ARR[2]#0} ))

```
