Search code examples
bashshellstdinttystty

How to set stty -echo and also read from /dev/stdin


I am trying to write a program which reads from stdin and also receives user input from the tty. I would like to disable rendering of user input because it messes with my menu system and causes flickering if I redraw to remove it. However I cannot seem to use stty -echo if the script recieves input from stdin.

Here is a simplified example of the script:

trapinput

#!/bin/bash

hideinput()
{
  if [ -t 0 ]; then
     echo "Is tty"
     save_state=$(stty -g)
     stty -echo -icanon time 0 min 0
     echo -ne "\e[?1049h\r" 1>&2;
  else
     echo "is not tty"
  fi
}

cleanup()
{
  if [ -t 0 ]; then
    stty "$save_state"
    echo -ne "\e[?1049l" 1>&2;
    echo "exit tty"
  else
    echo "is not tty"
  fi
}

trap cleanup EXIT
trap hideinput CONT
hideinput

input="$(< /dev/stdin)";
echo "$input"
while true;
do
  read -r -sn1 < /dev/tty;
  read -r -sn3 -t 0.001 k1 < /dev/tty;
  REPLY+=$k1;
  echo $REPLY
done

hello.txt

helloworld!

running $ ./trapinput will echo "Is tty" on start and "exit tty" when killed along with running the rest of the program as I would expect. It also prevents the user input from being displayed directly allowing me to print it on the screen in the correct location.

However if I run $ echo "test" | ./trapinput or $ ./trapinput < hello.txt it will echo "is not tty" and stty -echo is not set causing user input to be displayed where I do not want it.

How can I disable rendering of user input but retain the ability to pipe in text/use file redirection?


Solution

  • How can I disable rendering of user input but retain the ability to pipe in text/use file redirection?

    Disable the echo on where you from take the input. Do:

    trap 'cleanup < /dev/tty' EXIT
    trap 'hideinput < /dev/tty' CONT
    hideinput </dev/tty
    

    You could also open a file descriptor specific for input exec 10</dev/tty, etc.