Bash: Prompting for user input while reading file

bashreadstdin

I'm working on a bash script that parses a tab separated file. If the file contains the word "prompt" the script should ask the user to enter a value.

It appears that while reading the file, the "read" command is not able to read from standard input as the "read" is simply skipped.

Does anybody have a work around for doing both reading from a file as well as from stdin?

Note: The script should run on both Git Bash and MacOS.

Below is a little code example that fails:

#!/bin/bash

#for debugging
set "-x"


while IFS=$'\r' read -r line || [[ -n "$line" ]]; do
  [[ -z $line ]] && continue

  IFS=$'\t' read -a fields <<<"$line"

  command=${fields[0]}

  echo "PROCESSING "$command
  if [[ "prompt" = $command ]]; then
    read -p 'Please enter a value: ' aValue
    echo
  else 
    echo "Doing something else for "$command
  fi
done < "$1"

Output:

$ ./promptTest.sh promptTest.tsv
+ IFS=$'\r'
+ read -r line
+ [[ -z something       else ]]
+ IFS=' '
+ read -a fields
+ command=something
+ echo 'PROCESSING something'
PROCESSING something
+ [[ prompt = something ]]
+ echo 'Doing something else for something'
Doing something else for something
+ IFS=$'\r'
+ read -r line
+ [[ -z prompt ]]
+ IFS=' '
+ read -a fields
+ command=prompt
+ echo 'PROCESSING prompt'
PROCESSING prompt
+ [[ prompt = prompt ]]
+ read -p 'Please enter a value: ' aValue
+ echo

+ IFS=$'\r'
+ read -r line
+ [[ -n '' ]]

Sample tsv file:

$ cat promptTest.tsv
something       else
prompt
otherthing       nelse

Best Answer

The simplest way is to use /dev/tty as the read for keyboard input.

For example:

#!/bin/bash

echo hello | while read line
do
  echo We read the line: $line
  echo is this correct?
  read answer < /dev/tty
  echo You responded $answer
done

This breaks if you don't run this on a terminal, and wouldn't allow for input to be redirected into the program, but otherwise works pretty well.

More generally, you could take a new file handle based off the original stdin, and then read from that. Note the exec line and the read

#!/bin/bash

exec 3<&0

echo hello | while read line
do
  echo We read the line: $line
  echo is this correct?
  read answer <&3
  echo You responded $answer
done

In both cases the program looks a bit like:

% ./y
We read the line: hello
is this correct?
yes
You responded yes

The second variation allows for input to also be redirected

% echo yes | ./y
We read the line: hello
is this correct?
You responded yes
Related Question