4

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
  • read is reading from standard input -- you've redirected the first parameter to the script ($1) as standard input, so it isn't the keyboard any more. You can't use read to read from both the file and from the keyboard at the same time. – Andy Dalton Jul 5 '18 at 14:49
  • Is there a way to temporarily redirect the input back to stdin if "prompt" is found? – Bernie Lenz Jul 5 '18 at 14:51
3

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
  • The "read answer < /dev/tty" approach works beautifully. Also interesting is the approach to create a new "handle" with "exec 3<&0" – Bernie Lenz Jul 5 '18 at 15:18
1

This SO Q&A titled: How to read from a file or stdin in Bash? shows this method which highlights how to do what it sounds like you're looking for:

while read line
do
  echo "$line"
done < "${1:-/dev/stdin}"
1

You can dup standard input before you do the redirection.

#!/bin/bash
# Assuming here fd 4 is unused.  Dup file descriptor 0 (stdin) to
# file descriptor 4
exec 4<&0 

while read x; do # Read from stdin (the file b/c of redirect below)
    echo "From $1: $x"
    read y <&4 # Read from file descriptor 4 (the original stdin)
    echo "From keyboard: $y"
done < "$1"

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.