5

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 
2
  • 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.CommentedJul 5, 2018 at 14:49
  • Is there a way to temporarily redirect the input back to stdin if "prompt" is found?CommentedJul 5, 2018 at 14:51

3 Answers 3

7

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 
1
  • The "read answer < /dev/tty" approach works beautifully. Also interesting is the approach to create a new "handle" with "exec 3<&0"CommentedJul 5, 2018 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" 

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.