Reputation: 244
One thing about the while loop work.
I have following code:
f2(){ if [[ -t 0 ]] ; then echo "$*" ; else echo $(cat) ; fi ; }
echo -e 'a\nb\nc' | while read var ; do f2 $var ; done
What is interesting to me, is why the 'a' letter does not printed. However if i use the for loop, everything seem to work as expected. Like this:
for var in `echo -e 'a\nb\nc'`; do f2 $var ; done
It is nedded to preserve these echoes, because them are replacements for other scripts that give output on stdout, but them give the exactly same result. Some values that should be printed are missing.
Thank you all in advance.
Upvotes: 0
Views: 119
Reputation: 241931
Let's take a careful look at this line:
echo -e 'a\nb\nc' | while read var ; do f2 $var ; done
That:
echo
command, redirecting its stdout to the pipewhile
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.
Upvotes: 3
Reputation: 941
I believe 'a' gets into var, and the rest of the stdin gets consumed/processed by cat.
Perhaps the following example can help visualize this:
In this example, only 'read' consumes the stdin.
echo -e 'a\nb\nc' | while read var ; do echo Iterated $var ; done
Iterated a
Iterated b
Iterated c
With cat, read consumes the first portion of the stdin, and the rest gets consumed by cat.
$ echo -e 'a\nb\nc' | while read var ; do echo $(cat); echo Iterated $var ; done
b c
Iterated a
Upvotes: 0