Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BASH - Timed Input - Show countdown

Tags:

bash

input

I have a bash script that asks the user for their details.

I'm setting a limit to how long we wait for the input. I've found this and it appears to what I want.

timelimit=5
echo -e " You have $timelimit seconds\n Enter your name quickly: \c"
name=""
read -t $timelimit name
#read -t $timelimit name <&1 
# for bash versions bellow 3.x
if [ ! -z "$name" ]
then
echo -e "\n Your name is $name"
else
echo -e "\n TIME OUT\n You failed to enter your name"
fi 

It shows "You have 5 seconds..." any way to update the output so it shows 4,3,2,1 etc as it counts down ?

Thanks

like image 374
Rocket Avatar asked Oct 30 '25 04:10

Rocket


2 Answers

I have tried most of these answers and none of them worked perfectly for me.
Been playing with this for a local developer deployment script.
This solves a few of the issues noted, like including printed output, etc.
Also wrapped as a function for portability. I'm keen to see any improvements.

Here is my solution:

#!/bin/bash
# set -euo pipefail

function read_yn {
  MESSAGE=$1
  TIMEOUTREPLY=$2
  READTIMEOUT=$3
  NORMALREPLY="Y"
  if [ -z "${TIMEOUTREPLY}" ]; then
      TIMEOUTREPLY="Y"
  fi
  if [ -z "${READTIMEOUT}" ]; then
      READTIMEOUT=5
  fi
  TIMEOUTREPLY_UC=$( echo $TIMEOUTREPLY | awk '{print toupper($0)}' )
  TIMEOUTREPLY_LC=$( echo $TIMEOUTREPLY | awk '{print tolower($0)}' )
  if [ "${TIMEOUTREPLY_UC}" == "Y" ]; then
    NORMALREPLY="N"
  fi
  NORMALREPLY_UC=$( echo $NORMALREPLY | awk '{print toupper($0)}' )
  NORMALREPLY_LC=$( echo $NORMALREPLY | awk '{print tolower($0)}' )
  for (( i=$READTIMEOUT; i>=1; i--)); do
    printf "\r${MESSAGE} [${NORMALREPLY_UC}${NORMALREPLY_LC}/${TIMEOUTREPLY_UC}${TIMEOUTREPLY_LC}] ('${TIMEOUTREPLY_UC}' in ${i}s) "
    read -s -n 1 -t 1 WAITREADYN
    if [ $? -eq 0 ]; then
      if [[ "${WAITREADYN}" = "y" || "${WAITREADYN}" = "Y" || "${WAITREADYN}" = "n" || "${WAITREADYN}" = "N" ]]; then
        break
      else
        sleep 1
      fi
    fi
  done
  printf "\r${MESSAGE} [${NORMALREPLY_UC}${NORMALREPLY_LC}/${TIMEOUTREPLY_UC}${TIMEOUTREPLY_LC}] ('${TIMEOUTREPLY_UC}' in 0s)      "
  yn=""
  if [ -z $WAITREADYN ]; then
    echo -e "\nNo input entered: Defaulting to '${TIMEOUTREPLY_UC}'"
    yn="${TIMEOUTREPLY_UC}"
  else
    echo -e "\n${WAITREADYN}"
    yn="${WAITREADYN}"
  fi
}

read_yn "TESTING" "y" 5

GIST: https://gist.github.com/djravine/7a66478c37974940e8c39764d59d35fa

LIVE DEMO: https://replit.com/@DJRavine/read-input-with-visible-countdownsh?v=1

like image 140
Adan Rehtla Avatar answered Nov 01 '25 19:11

Adan Rehtla


This should work and shouldn't overwrite input, bit more long winded than the other solutions.

#!/bin/bash

abend() 
{       
        stty sane
        exit
        #Resets stty and then exits script
}

DoAction(){

        stty -echo 
        #Turn off echo
        tput sc
        #Save cursor position
        echo -ne "\033[0K\r"
        # Remove previous line
        tput cuu1
        #Go to previous line
        tput el
        #clear to end of line
        echo "You have $(($time-$count)) seconds"
        #Echo timer
        echo -n "$Keys"
        #Echo currently typed text
        stty echo
        #turn echo on
        tput rc
        #return cursor
}


main()
{
trap abend SIGINT # Trap ctrl-c to return terminal to normal
stty -icanon time 0 min 0 -echo
#turn of echo and set read time to nothing
keypress=''

time=5
echo "You have $time seconds"
while Keys=$Keys$keypress; do
        sleep 0.05
        read keypress && break
        ((clock  = clock + 1 ))
        if [[ clock -eq 20 ]];then
                ((count++))
                clock=0
                DoAction $Keys
        fi
        [[ $count -eq $time ]] && echo "you have run out of time" && abend



done

stty sane
echo Your username was $Keys
echo "Thanks for using this script."
exit 0
}

main
like image 22
123 Avatar answered Nov 01 '25 20:11

123