1

UPDATE - looks like this is something peculiar with bash 4.3.42 as it works fine with 4.3.46. Leaving this post for those who run into the same issue in future.

When I run this command in bash command line it works properly:

% (TMOUT=3; s="no selection"; select s in a b c ; do break ; done; echo $s)
1) a
2) b
3) c
#?
no selection
% _

Result: displays no selection and comes back to command line.

When, however, I place it a script, and execute it, it repetitively requests selection.

% cat a.sh
#!/bin/bash
(TMOUT=3; s="no selection"; select s in a b c ; do break ; done; echo $s)

% ./a.sh

Result:

1) a
2) b
3) c
#? 1) a
2) b
3) c
#? 1) a
2) b
3) c
#? ^C
% _

Why it so? My main question is - how to make it work in a script?!

UPDATED

% bash --version
GNU bash, version 4.3.42(1)-release (x86_64-unknown-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.

% uname
Linux lx1 2.6.32-642.6.2.el6.x86_64 #1 SMP Mon Oct 24 10:22:33 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux
5
  • 1
    I just tried it and it works for me using a BASH script on a BASH shell (i.e. timeout after 3 seconds). I can't reproduce your problem – Jason K Lai Jun 7 '19 at 19:12
  • I can't reproduce this either. There must be something odd about your setup. What operating system are you using? What version of bash (bash --version)? – terdon Jun 7 '19 at 19:14
  • Must be something peculiar with 4.3.42 as 4.3.46 works fine for me, too. – Grzegorz Jun 7 '19 at 20:24
  • There was a bugfix in release 4.2 regarding select not terminating properly when TMOUT was set, but that's older than the bash you're using. – Kusalananda Jun 7 '19 at 20:51
  • @Kusalananda - That is interesting there was a bug like that. Maybe they re-introduced it in me version... Good to know I am not insane :D – Grzegorz Jun 7 '19 at 22:34
1

From manual:

TMOUT If set to a value greater than zero, TMOUT is treated as the default timeout for the read builtin. The select command terminates if input does not arrive after TMOUT seconds when input is coming from a terminal. In an interactive shell, the value is interpreted as the number of seconds to wait for input after issuing the primary prompt. Bash terminates after waiting for that number of seconds if input does not arrive.

So first case is due to interactive shell.

$ ps -aef|grep bash; echo "before"; (TMOUT=3; s="no selection"; ps -aef|grep bash; select s in a b c ; do break ; done; echo $s;ps -aef|grep bash);echo "after";ps -aef|grep bash
asktyagi    4926 23767  0 09:40 pts/0    00:00:00 grep --color=auto bash
asktyagi   23767 23741  0 09:03 pts/0    00:00:00 -bash
**before**
asktyagi    4927 23767  0 09:40 pts/0    00:00:00 -bash
asktyagi    4929  4927  0 09:40 pts/0    00:00:00 grep --color=auto bash
asktyagi   23767 23741  0 09:03 pts/0    00:00:00 -bash
1) a
2) b
3) c
#?
no selection
asktyagi    4927 23767  0 09:40 pts/0    00:00:00 -bash
asktyagi    4931  4927  0 09:40 pts/0    00:00:00 grep --color=auto bash
asktyagi   23767 23741  0 09:03 pts/0    00:00:00 -bash
**after**
asktyagi    4933 23767  0 09:40 pts/0    00:00:00 grep --color=auto bash
asktyagi   23767 23741  0 09:03 pts/0    00:00:00 -bash

Now with script

$ cat a.sh
#!/bin/bash
(TMOUT=3; s="no selection"; select s in a b c ; do break ; done; echo $s)
ps -aef|grep bash

$ sh a.sh
1) a
2) b
3) c
#?
no selection
asktyagi    5201  5188  0 09:41 pts/0    00:00:00 grep bash
asktyagi   23767 23741  0 09:03 pts/0    00:00:00 -bash
3
  • The primary prompt is not issued while running a script. In other words, the shell running a script is not an "interactive shell". – Kusalananda Jun 8 '19 at 9:19
  • Edited my answer with more explanation, thanks. – asktyagi Jun 8 '19 at 9:46
  • @asktyagi Thank you for the detailed response. It still does not answer how to fix the issue, and it looks like there is no solution for that because, as I have updated my question, there seems to be a bug in bash. – Grzegorz Jun 10 '19 at 18:34
0

Use the timeout command. This example could be improved but it makes the point. First use the bash-builtin trap command to run something on exit, then run your loop. It runs as follows:

#] timeout 3s sh -c 'trap "echo no selection" EXIT; select s in a b c ; do break ; done; '
1) a
2) b
3) c
#? <waits 3 seconds>no selection
3
  • Thanks for the update! I have written my own select but out of curiosity I checked your solution. If you run this in the command line and do selection you will end up 'no selection' printed out anyway. If you run it in a script and do selection, not only you will get 'no selection' printed out but also your selection (let's say 1) will be executed as a command. – Grzegorz Sep 5 '20 at 16:23
  • I have posted my solution I have ultimately wrote. Thanks for your interest and time! – Grzegorz Sep 5 '20 at 16:30
  • I suppose you could clear the trap on successful exit: trap - EXIT. Thanks for posting your solution! – KJ7LNW Sep 5 '20 at 23:12
0

So ultimately I have written my own selection that can be used in a script.

# $1 timeout
# rest - selection
function select_ {
  t=$1
  shift
  arr=($*)
  i=1
  for a in $*; do
    echo "$i) $a" >$(tty)
    ((i++))
  done
  echo "TIMEOUT: In ${t} seconds ${arr[0]} will be automatically selected." >$(tty)
  printf "#? " >$(tty)
  read -t $t x
  [ "$x" == "" ] && {
     x=1
     echo "$x" >$(tty)
  }
  ((x=x+0))
  [ "$x" -ge 1 -a "$x" -le ${#arr[@]} ] && {
    ((x--))
    echo ${arr[$x]}
  }
}

How to use it:

select_ 10 a b c

Example:

$ ./x.sh
1) a
2) b
3) c
TIMEOUT: In 10 seconds a will be automatically selected.
#? 2
b

$ ./x.sh
1) a
2) b
3) c
TIMEOUT: In 10 seconds a will be automatically selected.
#? 1
a

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.