while loop with timeout and enter-less input? SOLVED

About writing shell scripts and making the most of your shell
Forum rules
Topics in this forum are automatically closed 6 months after creation.
Locked
User avatar
linx255
Level 5
Level 5
Posts: 668
Joined: Mon Mar 17, 2014 12:43 am

while loop with timeout and enter-less input? SOLVED

Post by linx255 »

I would like to be able to end the following while loop after 5 seconds:

Code: Select all

while true; do
  read -p "" -n 1 key
  case $key in
    [xX]) y="y"; break ;;
  esac
done
However, when I try changing the while statement to:

Code: Select all

t=$((SECONDS+5))
while [ $t -lt 5 ]; do
the read command then pauses each loop until ENTER is pressed, so this stalling renders the countdown useless.

Any ideas?
Thanks :D
Last edited by LockBot on Wed Dec 28, 2022 7:16 am, edited 2 times in total.
Reason: Topic automatically closed 6 months after creation. New replies are no longer allowed.
- I'm running Mint 18 Mate 64-bit
- 4.15.0-34-generic x86_64
- All my bash scripts begin with #!/bin/bash
WharfRat

Re: while loop with timeout and enter-less input?

Post by WharfRat »

I don't know I understand what you're looking for but paste this in its entirety in a terminal and press enter:

Code: Select all

SECOND=0
while [ $SECOND -lt 5 ]; do
  sleep 1s
  SECOND=$((SECOND+1))
done
echo "SECOND is now $SECOND"
User avatar
xenopeek
Level 25
Level 25
Posts: 29590
Joined: Wed Jul 06, 2011 3:58 am

Re: while loop with timeout and enter-less input?

Post by xenopeek »

Do away with the loop entirely. Use the timeout option for your read command:

Code: Select all

read -p "" -n 1 -t 5 key
From the bash manpage:

Code: Select all

-t timeout
       Cause read to time out and return failure if a complete line of input (or a specified number of char‐
       acters)  is  not read within timeout seconds.  timeout may be a decimal number with a fractional por‐
       tion following the decimal point.  This option is only effective if read is reading input from a ter‐
       minal,  pipe, or other special file; it has no effect when reading from regular files.  If read times
       out, read saves any partial input read into the specified variable  name.   If  timeout  is  0,  read
       returns  immediately, without trying to read any data.  The exit status is 0 if input is available on
       the specified file descriptor, non-zero otherwise.  The exit status is greater than 128 if the  time‐
       out is exceeded.
Image
lmuserx4849

Re: while loop with timeout and enter-less input?

Post by lmuserx4849 »

A couple of thoughts:
  • Debug: At the top of your script add 'set -x' and run to see what is happening.
  • Debug: Also add 'set -u' to catch any unset variables.
  • Debug: Add some quick echos. For example, right after the read, what is the return code 'echo $?'
    The return codes are documented.
  • Documentation: On the command line do a 'help read'. You can do that for all bash builtins. To see if it is a builtins do: 'type -a READ'. The same info, or more is in the bash man page. If you'd rather read the man page in html checkout 'man --html bash' (you may need to specify the brower, see 'man man').
  • As mentioned, if you want a timeout on read use '-t'
  • You can get fancier (do you need to??) and use a subprocess. See: http://mywiki.wooledge.org/ProcessManagement

    It would look something like:

    Code: Select all

    #!/bin/bash
    #set -xu
    
    declare -- sleepPID=0
    declare -- subprocessPID=0
    
    function my_loop() {
      #lsof -p $$
      local -- key=''
      local -- running=true
    
      printf -- '%s\n' "${FUNCNAME}"
    
      # Handle ctrl-c. Not inherited
      trap 'running=false' INT
    
      while ${running}; do
        # FD are inherited
        read -u 3 -rp "==> " key
        case ${key} in
          [xX]) y="y"; break ;;
        esac
      done
    }
    
    # Handle ctrl-c
    function cleanup() {
      while kill -0 ${subprocessPID}; do
        kill ${subprocessPID}
        sleep 1
      done
    
      while kill -0 ${sleepPID}; do
        kill ${sleepPID}
        sleep 1
      done
    
      trap - INT
    }
    
    trap cleanup INT
    
    exec 3</dev/tty
    my_loop &
    subprocessPID=$!
    
    sleep 10s &
    sleepPID=$!
    
    printf -- 'Sleep pid is [%s]\n' ${sleepPID}
    printf -- 'Subprocess pid is [%s]\n' ${subprocessPID}
    
    wait ${sleepPID}
    kill ${subprocessPID}
    
    trap - INT
    
User avatar
linx255
Level 5
Level 5
Posts: 668
Joined: Mon Mar 17, 2014 12:43 am

Re: while loop with timeout and enter-less input?

Post by linx255 »

Thanks y'all !!! Y'all ROCK !!! :D :D :D

Code: Select all

read -p "" -n 1 -t 5 key
works perfectly!
- I'm running Mint 18 Mate 64-bit
- 4.15.0-34-generic x86_64
- All my bash scripts begin with #!/bin/bash
Locked

Return to “Scripts & Bash”