Subshell IO redirection
Asked Answered
S

1

12

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.

Superego answered 24/9, 2013 at 13:11 Comment(0)
T
11

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.)

Tallage answered 24/9, 2013 at 13:33 Comment(3)
+1 for very good explanation. However, the OP is asking if it is possible to get such behaviour without any temporary file. Is it? My tests were unsuccessful.Arbiter
By replacing head with sh -c "read line; echo "$line"', the desired behavior can be accomplished without a temporary file.Tallage
+1 That's the exact explanation I was looking for! Since you talked about creating own things, I've been thinking about writing a shell script of my own, with a couple of friends; we're thinking about a somehow functional shell, with pattern matching and all of the very coolest trickstery (:Superego

© 2022 - 2024 — McMap. All rights reserved.