1

TARGET

Until some sub-task in script completes its job:

  • stop echo;
  • disable cursor;
  • consume all user input;
  • do not block interrupts (Ctrl+C, etc.).

WHAT HAVE DONE

For now, using this answer, I create few functions for that, here they are:

function hide_input()
{
  if [ -t 0 ]; then
    stty -echo -icanon time 0 min 0
  fi
}

function reset_input()
{
  if [ -t 0 ]; then
    stty sane
  fi
}

function stop_interactive()
{
  trap reset_input EXIT
  trap hide_input CONT
  hide_input
  tput civis
}

function start_interactive()
{
  tput cnorm
  reset_input
}

function consume_input()
{
  local line
  while read line; do line=''; done
}

And here is the way they are used:

echo "Warn the user: the job will be started."
read -p "Continue? [yes/no] > "
if [ "$REPLY" == "yes" ]; then
  stop_interactive  # <== from here all input should be rejected
  echo "Notify the user: job starting..."

  # << ------ here goes some long job with output to terminal ------>

  echo "Notify the user: job done!"
  consume_input # <== here I trying to get all user input and put nowhere
  start_interactive # <== from here restore normal operation
else
  echo "Aborted!"
  exit 0
fi

THE PROBLEM

The problem is: current 'solution' does not work. When I press keys during long running job they appears on the screen, and pressing 'Enter' ruins all output with cursor movements. Furthermore, after 'start_interactive' function call all input appears on the terminal screen.

What is the right solution for this task?

SOLUTION

Final working solution is:

function hide_input()
{
  if [ -t 0 ]; then
    stty -echo -icanon time 0 min 0
  fi
}

function reset_input()
{
  if [ -t 0 ]; then
    stty sane
  fi
}

function consume_input()
{
  local line
  while read line; do line=''; done
}

function stop_interactive()
{
  trap reset_input EXIT
  trap hide_input CONT
  hide_input
  tput civis
}

function start_interactive()
{
  consume_input
  trap - EXIT
  trap - CONT
  tput cnorm
  reset_input
}

echo "Warn the user: the job will be started."
read -p "Continue? [yes/no] > "
if [ "$REPLY" == "yes" ]; then
  stop_interactive
  echo "Notify the user: job starting..."
  do_the_job &
  pid=$!
  while ps $pid > /dev/null ; do
    consume_input
  done
  echo "Notify the user: job done!"
  start_interactive
else
  echo "Aborted!"
  exit 0
fi

1 Answer 1

2

Based in your question, there are many questions "why" if I look at your code. If you do not want to change the behavior of ^C etc., then don't use traps. All your functions test whether the file descriptor 0 is a terminal. Do you plan to use the script in a pipe? Also, your consuming of user input will continue until end-of-file, so the script may never end.

Based on your question, I would write something like this:

echo "Warn the user: the job will be started."
read -p "Continue? [yes/no] > "
if [ "$REPLY" == "yes" ]; then
    stty -echo
    echo "Notify the user: job starting..."
    program_to_execute &
    pid=$!
    while ps $pid > /dev/null ; do
        read -t 1 line
    done
    echo "Notify the user: job done!"
else
    echo "Aborted!"
fi
stty sane
Sign up to request clarification or add additional context in comments.

5 Comments

it's strange, but I got a error when put 'stty -echo' to script: stty: 'standard input': Inappropriate ioctl for device when I run it from terminal all works fine, just when inside script
thank you for background job suggestion, it will be useful to run input consumer in main job
scrip will not freeze, because -icanon sets zero read timeout
the problem was that I piping 'yes' answer to the script!
That makes sense; [ -t 0 ] will fail and your sttys never get executed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.