Code:
while read line;
do
tr 3 4
done<<EOF
1
2
3
4
EOF
Produces:
2 4 4
Where does the 1 go?
Code:
while read line;
do
tr 3 4
done<<EOF
1
2
3
4
EOF
Produces:
2 4 4
Where does the 1 go?
$ while read line
> do
> tr 3 4
> done<<EOF
> 1
> 2
> 3
> 4
> EOF
2
4
4
What happens here is that read line
gets the first line with the 1.
The rest of the input is passed to tr
, which replaces 3 with 4, so 2 3 4 becomes 2 4 4
The 1 goes into a variable line. Example
$ while read foo; do echo "This is $foo"; done <<EOF
> madness
> SPARTAAAAAAAAAAAA
> EOF
This is madness
This is SPARTAAAAAAAAAAAA
The strange behavior you see is because read is a builtin, and is somewhat special. When you perform piping, for example, suppose you write
echo "foo bar" | read i
You would expect something in $i, right ? try it, it will be empty. Why? When you do a piping, each command of the pipe runs in a subshell. This is true for everything, and always.
read is kind of strange, because it is not a command, it is a builtin of the shell, and it has to export a variable so that you can see it. If you use the pipe, like in the example above, it will export i
in a subprocess (a subshell), and you will never see it because the shell environment you are working in is the parent, and a subshell cannot set the environment of its parent.
In your case, what happens is that read
, together with while
, as builtins, are acting in your shell. read
dutifully does its job of taking the first thing coming from stdin and setting it into the variable of the same shell. This removes the 1 from the stdin, because this is what the read builtin does. The stdin is now left with the remaining stuff, which is passed to tr because in the while, the stdin is intercepted by the first command. Try this
$ while true; do rev; echo "x"; done <<EOF
> hello
> EOF
olleh
you will remain stuck with a pile of x. rev has already completed (check its output in the firsts lines), and the stdin is closed (you did it with EOF). the while loop will continue forever (and you will get a cascade of x) but you cannot provide anything anymore to rev, because you are not typing to the stdin anymore.
Stefano's answer is very good. Maybe you want to do this?
tr 3 4 <<EOF
1
2
3
4
EOF
this is a followup on Stephano's answer - all credit should go to him. here is how you can understand this:
first you asked sh to 'read' a line, so it did. then you asked it do 'tr 3 4'. now, 'read' only reads one line, but 'tr' will read all the input until it was finished, so when you get back to 'read' there is nothing more in stdin and the program finished. since you never told sh to echo $line, you wont get 1 in the output.
the following should print 1 2 4 4
while read line
do
echo $line | tr 3 4
done