The problem with your command is that only the 1st command - head -1 - receives the stdin input, because it consumes it on reading, so that the 2nd command - tail -1 - receives no input.
In this particular case, you can use command grouping ({ ...; ...; }):
{ head -1; tail -1; } < text.txt
Caveats:
The above only works with seekable input, meaning either a regular file, a here-string or here-doc.
- It will not work with pipeline input (
cat text.txt | { head -1; tail -1; }) or with input from a process substitutions ({ head -1; tail -1; } < <(cat text.txt)), because such input is not seekable (tail cannot scan from the end backward).
Even with seekable input this is not a generic method to send input to multiple commands at once.
- The above only works because
tail reads backwards from the end of the (seekable) input, irrespective of whether all the input has already been read or not.
As a simpler alternative that works generically, here's a sed solution:
sed -n '1p; $p' text.txt
-n suppresses output of lines by default 1p matches line 1 and prints it (p). $p matches the last line ($) and prints it.