1
  1. In while <command> part of a while loop, is <command> executed in each loop independently of other loops?
  2. In the following example, why can the while loop make read read all the lines of a file one by one, while without the while loop, read always read the first line of the file in every repetition?

    Without using the while loop, can read read all the lines of the file one by one?

    $ cat /tmp/tt
    1 2 3 4 5
    6 7 8 9
    10 11
    
    $ cat /tmp/tt | while read tt; do echo $tt; done
    1 2 3 4 5
    6 7 8 9
    10 11
    
    $ cat /tmp/tt | read tt
    $ echo $tt
    1 2 3 4 5
    $ cat /tmp/tt | read tt
    $ echo $tt
    1 2 3 4 5
    
  • 2
    man read: 'The read utility shall read a single line from standard input.'. Regarding your experiments, first try clear out the variable tt, it seems to me tt has a value set already. The way you read tt from a pipe and echoing tt as a separete command, should result in a empty string, because piped commands execute in their own subshell, the variable value is lost after the pipe exits. While loop should work because you are echoing the variable from within the subshell. – Vikyboss Feb 10 '16 at 3:43
1

When you have two command-lines with two read instances of cat outputs, then only the first line can be read, in the way you have used, because STDIN for read will always have the same contents with the same first line.

If you read the contents of one cat output multiple times, you will get the next lines, because STDIN for read will not get reset to original starting line.

$ cat /tmp/tt | ( read tt ; echo $tt )
1 2 3 4 5

$ cat /tmp/tt | ( read tt ; echo $tt ; read tt ; echo $tt )
1 2 3 4 5
6 7 8 9

$ cat /tmp/tt | ( read tt ; echo $tt ; read tt ; echo $tt ; read tt ; echo $tt )
1 2 3 4 5
6 7 8 9
10 11

In short : "(A) If you execute read once, twice & thrice on the same input (STDIN), you will get the first second & third lines. (B) If you read many times, but reset the input for every read, then you will only get the same first line many times"

More Info : By "grouping" the read instances (on the left of the pipe), the sub-shell keeps the pipe open. When using while, again the sub-shell keeps the pipe open. Hence, read will read the "next line" every time, rather than the "first line".

  • thanks. Is it right that the commands in pipeline run in subshells, and the grouping of the read and echo commands further run in subshells of the subshells? – Tim Feb 11 '16 at 4:00
  • Correct, sub-shell handles the pipe. in the pipe there may be commands which run as child-processes of the sub-shell. – Prem Feb 11 '16 at 4:12
  • I am not just talking about subprocess of the subshell for pipe. Grouped commands by parenthese also create subshells for the commands to run in. So is the following hierarchy of processes correct: the shell process where you run the commands with the pipe -> the subshell process for the pipe -> the subshell by parentheses group -> the subprocesses for read and for echo? – Tim Feb 11 '16 at 4:19
  • Correct !! Reference : tldp.org/LDP/abs/html/subshells.html for more about sub-shells. – Prem Feb 11 '16 at 4:50
3

Q1

In while <command> part of a while loop, is <command> executed in each loop independently of other loops?

Yes, <command> will be executed for each loop of the while loop executed.

How many loops are executed is affected by other loops if other loops also consume the stdin input. The read command reads from stdin (unles option -u n is used). Some other read may consume the same input, that will affect the first while loop.

Q2

A new code, as yours has a big fault [1].

$ cat /tmp/tt | {  read tt; echo "$tt";   }
1 2 3 4 5
$ cat /tmp/tt | {  read tt; echo "$tt";   }
1 2 3 4 5

The cat command will always start at the beginning of a file.
The read command (without the -d option set to something else from new line ($'\n')) will get the first line and will exit (causing the pipe to close).

In a while loop even if read exists, there is still code to be executed (the while) that will keep the pipe open to get the next line from the cat command.

$ cat /tmp/tt | while read tt; do echo $tt; done
1 2 3 4 5
6 7 8 9
10 11

[1] your code must clean $tt, try unset tt; cat /tmp/tt |{ read tt; echo "$tt"; } to confirm.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.