Question

Given a file "foo.txt", created from:

$ seq 1 10 > "foo.txt"

I'm trying to read both the first and last line of the file, and I started by redirecting the file to a subshell, so that the list of commands will consume the file in sequence -- each command consumes the input from the point the last command has left it, I suppose:

$ (head -1; tail -1) < "foo.txt"
1
10

However, if I try the same with a temporary file descriptor, the output is not the same:

$ (head -1; tail -1) < <(seq 1 10)
1

Questions:

  1. I'm not sure of what to name <(seq 1 10), but what is the difference between this temporary file descriptor and a regular one ("foo.txt"), that causes the subshell execution to behave differently?

  2. How can I achieve the same behavior of redirecting a "foo.txt" to the subshell, but without a temporary file -- without actually creating a file.

Was it helpful?

Solution

It's just a buffering issue. Try:

cat foo.txt | ( head -1; tail -1)

and you will (probably) see the same thing (head consumes the entire input). Probably what is happening is that head is behaving differently with a regular file than with a pipe, and the process substitution (which is the name for the <(cmd) syntax) is creating a pipe. (In Linux, the head from Gnu coreutils does an lseek to try to reposition at the final newline that it outputs, but the lseek fails on a pipe.)

Also try:

(head -1; tail -1) < <(cat /usr/share/dict/words)

Here, head only consumes the first page of input, and tail sees the final line of the data.

The behavior you are seeing is not well defined, since there is no standard that dictates how much of the data head is allowed to consume. If you replace head with sh -c 'read line; echo "$line"', then you will get the desired behavior in all cases. (Or write your own tool to provide the behavior of head that you want -- printing a fixed number of lines while not consuming any more than that.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top