120

I am having a bash script which is something like following,

cat filename | while read line
do
    read input;
    echo $input;
done

but this is clearly not giving me the right output as when I do read in the while loop it tries to read from the file filename because of the possible I/O redirection.

Any other way of doing the same?

oguz ismail
  • 1
  • 16
  • 47
  • 69
w2lame
  • 2,774
  • 6
  • 35
  • 48
  • Same thing happens when you switch user in bash and run read command under switched user in script – krupal Sep 20 '13 at 12:16

6 Answers6

126

Read from the controlling terminal device:

read input </dev/tty

more info: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop

dank
  • 1,324
  • 1
  • 8
  • 2
  • 12
    -1, as this will circumvent any other redirect. For example, `bash yourscript < /foo/bar` will wait for user input, this is acceptable only when reading passwords. The answer by @GordonDavisson is preferable for all other uses. – tiwo Feb 06 '13 at 04:40
68

You can redirect the regular stdin through unit 3 to keep the get it inside the pipeline:

{ cat notify-finished | while read line; do
    read -u 3 input
    echo "$input"
done; } 3<&0

BTW, if you really are using cat this way, replace it with a redirect and things become even easier:

while read line; do
    read -u 3 input
    echo "$input"
done 3<&0 <notify-finished

Or, you can swap stdin and unit 3 in that version -- read the file with unit 3, and just leave stdin alone:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • why is your second script hanging? – Luca Borrione Feb 07 '13 at 17:41
  • 2
    @LucaBorrione: How are you using it? Is it waiting for you to give it input (note that `read line` is reading from notify-finished, but if you just run it as written `read -u 3 input` is reading from the console)? – Gordon Davisson Feb 07 '13 at 18:15
  • I get `read: 3: invalid file descriptor: Bad file descriptor` when tryiing to do `read -u 3` – maxprehl Apr 19 '23 at 20:31
  • @maxprehl I assume you're using the first version? Did you include the `3<&0` part in the redirect after `done` (but before the redirect from the input file)? That's what sets up file descriptor 3. – Gordon Davisson Apr 19 '23 at 20:50
5

Try to change the loop like this:

for line in $(cat filename); do
    read input
    echo $input;
done

Unit test:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done
dimba
  • 26,717
  • 34
  • 141
  • 196
4

I have found this parameter -u with read.

"-u 1" means "read from stdout"

while read -r newline; do
    ((i++))
    read -u 1 -p "Doing $i""th file, called $newline. Write your answer and press Enter!"
    echo "Processing $newline with $REPLY" # united input from two different read commands.
done <<< $(ls)
xerostomus
  • 466
  • 4
  • 11
  • This is not stdin. FD 1 which is not flushed and you can read from it. You also can use `read -u 2`. – Lajos Jun 15 '21 at 11:41
  • Well, if it is not an stdin, why can I not to use FD 12??? (I am not an expert. I only wonder.) – xerostomus Jun 19 '21 at 07:22
  • I was also wondering. but it seems because the FD 12 does not exist. FD 0 is the stdin. FD 1 stdout FD 2 error. it also works with the error descriptor as I wrote above. `read -u 2` – Lajos Jun 21 '21 at 14:20
  • 1
    Yes. The file with FD=12 is not open yet. I corrected the text. Thanks for the hit. :-) – xerostomus Jun 22 '21 at 18:15
2

It looks like you read twice, the read inside the while loop is not needed. Also, you don't need to invoke the cat command:

while read input
do
    echo $input
done < filename
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
  • 7
    The OP's goal is for the read inside the loop to come from the user, whereas the outer one is to read from the file. Thus, they legitimately want two different reads, from two different sources. This is clear both from the text of the question (describing the inner `read`'s behavior as "not right [because] it tries to read from the file `filename`") and their accepted answer. – Charles Duffy Feb 03 '17 at 16:15
-6
echo "Enter the Programs you want to run:"
> ${PROGRAM_LIST}
while read PROGRAM_ENTRY
do
   if [ ! -s ${PROGRAM_ENTRY} ]
   then
      echo ${PROGRAM_ENTRY} >> ${PROGRAM_LIST}
   else
      break
   fi
done
tripleee
  • 175,061
  • 34
  • 275
  • 318