20

I have the following bash script:

# do some time consuming task here
read -p "Give me some input: " input

Now as you might have guessed, if a user presses some random keys during the "time consuming task", the unwanted input is taken into account as well. How do I clear stdin (or at least ignore it) before I issue the read command?

2
  • 1
    Myself, unless you are writing a curses-like program, I find what you wish to do to be a design flaw in your program. UNIX/Linux has the very useful feature of buffering input ("type-ahead") and I commonly make use of this functionality. Coming across your program where you throw away what I typed, I would likely submit a bug and stop using your program until it was fixed.
    – Arcege
    Apr 29, 2011 at 12:13
  • 1
    Some users have annoying habit of playing piano with their keyboard while their program is busy doing something. I would rather throw away those keystrokes and start fresh. But you're right, the "type-ahead" is useful, but not always.
    – rabin
    Apr 29, 2011 at 15:52

7 Answers 7

18

I don't think there is a way to clear stdin but (with bash) you can read and discard what is there before you ask for the input

#do some time consuming task here
read -t 1 -n 10000 discard 
read -p "Give me some input: " input

This reads stdin and has a timeout of 1 second, it fails though if there are more than 10000 chars in stdin. I don't know how big you can make the nchars parameter.

4
  • I actually had found this hack on a forum. I was expecting to find a better way of doing it. Apparently not.
    – rabin
    Apr 28, 2011 at 16:09
  • @rabin: If you do find a better way post back here I have it in a few scripts.
    – user35787
    Apr 28, 2011 at 16:11
  • Unfortunately this doesn't work with all shells, e.g. dash :(
    – scai
    Dec 7, 2013 at 9:53
  • doesnt work anymore Mar 27 at 16:24
25

In Bash 4, you can set -t (timeout) to 0. In this case, read immediately returns with an exit status indicating whether there's data waiting or not:

# do some time consuming task here
while read -r -t 0; do read -r; done
read -p "Give me some input: " input
1
  • 1
    This only seems to work if the user pressed enter after the keys. If the user has just typed random keys (& no enter press), then the loop exits immediately without clearing the buffered characters.
    – btalb
    Mar 10, 2020 at 6:06
7
read -d '' -t 0.1 -n 10000

This reads multiple lines of inputs, if the user inadvertently pressed enter multiple times

7
function clear_stdin()
(
    old_tty_settings=`stty -g`
    stty -icanon min 0 time 0

    while read none; do :; done 

    stty "$old_tty_settings"
)

clear_stdin
4
  • 2
    If you leave out word "function", it would work by dash and by any other POSIX compliant shell.
    – jarno
    Mar 18, 2020 at 0:57
  • 1
    This is one of very few cases where you shouldn't double-quote a variable. Use stty $old_tty_settings without quotes. The reason is stty -g generates output that is unspecified. Implementations of stty are allowed to generate output with spaces and later expect the shell to split it and pass multiple arguments. What is specified is a constraint that the output of stty -g must be safe when unquoted, it must not trigger word expansion in a shell. Dec 1, 2020 at 15:36
  • @KamilMaciorowski I don't get it, is there a downside to the double quotes other than the fact that they aren't necessary?
    – kaan_a
    Aug 4, 2021 at 9:59
  • 3
    @kaan_a When quoted, the command may not work. It totally depends on the implementation of stty. The specification states the tool should understand its own output (i.e. the output of stty -g) as arguments (plural!) and the output shall not require quoting. If quoted, the string will be a single argument. The specification does not mention "argument" (singular), stty is not required to understand it in this form, it may or may not understand it. Not quoting is the only way that is required to work. Aug 4, 2021 at 13:30
5

this worked well for me:

function clean_stdin()
{
    while read -e -t 0.1; do : ; done
}
2
  • why -e in this case?
    – nhed
    Apr 18, 2017 at 18:33
  • @nhed not sure anymore, maybe to circumvent some system specific problem
    – pschichtel
    Apr 19, 2017 at 23:53
4

Enclose the time consuming task in a block whose stdin is closed:

{
     # time consuming task
} <&-

read -p "Give me some input: " input
4
  • I believe that this has nothing to do with the question. Dec 5, 2016 at 20:29
  • But it does! The user wants all input discarded. Not allowing input does exactly that -- since stdin is closed, all input is discard (by the system). It's an elegant solution to his problem. He gets the system to do the discard without the trouble of writing a discard loop. May 1, 2017 at 11:26
  • This seems to be the most reliable option so far. May 18, 2017 at 20:46
  • Seems perfect but does not work for me. I tried bash 5.0.0 and 4.4.19. read will still read the first line entered during # time consuming task. Furthermore, if the script doesn't contain any commands that read stdin after the read then the unread lines are executed on the interactive terminal after the script terminates. Did anyone successfully test this?
    – Socowi
    Feb 27, 2019 at 10:56
0

I long relied on @Илья Антипов's solution: thanks!
Until on a different setup, read blocked (= pause execution). So I ended up using cat.
And added back the double quotes, thanks to the context given by @Kamil Maciorowski.
This code seems to work:

clear_stdin () {
    old_tty_settings="$(stty -g)"
    stty -icanon min 0 time 0
    cat - > /dev/null
    stty "$old_tty_settings"
    unset old_tty_settings
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .