0

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.

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

thanks, for reply. what i need is that things would work without complications. such as if i read with while pipe into var, i want that this var no matter the means (script with test for the input type is terminal or pipe) would be echoed. that f2 is simple substitution for another script that inteded to not take care about input type would it be pipe or terminal and process data recieved. well, but what with the for loop, as i mentioned it produce result of require type. can i make the same with while loop?
"where f2 starts by checking if [[ -t 0 ]]: in other words, f2 checks to see if stdin is a terminal" - in what way can we see that in f2 function?
@user3694243: I don't understand your question about f2. You pasted the definition of f2 in the question; it clearly tests includes the [[ -t 0 ]] test (which, as I said, is the root of the problem).
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.