Let's take a careful look at this line:
echo -e 'a\nb\nc' | while read var ; do f2 $var ; done
That:
- Creates a pipe
- starts a subprocess to run the
echo command, redirecting its stdout to the pipe
- starts a subprocess to run the
while command, redirecting its stdin to the pipe.
So let's reduce that to its essentials, using this useful tool:
readlink -f /dev/stdin
readlink -f /dev/stdin (on Linux, anyway) will tell us what stdin resolves to. In an interactive shell, it will show that stdin is a terminal:
$ readlink -f /dev/stdin
/dev/pts/14
Now, let's go back to the pipeline:
$ echo | readlink -f /dev/stdin
/proc/28050/fd/pipe:[608866]
As expected, stdin is a pipe.
Now, the actual right-hand side of the pipe was:
| while read var ; do f2 $var ; done
where f2 starts by checking if [[ -t 0 ]]: in other words, f2 checks to see if stdin is a terminal. We can see that stdin is not a terminal, so f2 will proceed to the else clause:
echo $(cat)
which prints the rest of stdin, replacing line-endings with whitespace. The stdin redirect was originally three lines, a, b and c, but the read already read the first line, so what is left are the b and the c, and that's what cat will return.
Needless to say, the logic is wrong. Why would you want f2 to check whether its input had been redirected?
More normal might be to check if there are any arguments: if (($#)); then. But since I have no idea what you are trying to do, I can't provide a definitive solution.
Although in this case it would probably be better to fix f2, it is possible to avoid using stdin for the while read loop by using process substitution and a different file descriptor:
while read -u3 var ; do f2 $var ; done 3< <(echo $'a\nb\nc')
Here, 3< causes a redirect of file descriptor 3 (an unused file descriptor) and read -u3 tells read to read from fd 3 instead of stdin. <(command) is process substitution, which creates a name which can be used to read the output of the command.