Getting head to display all but the last line of a file: command substitution and standard I/O redirection
head -n -1
will give you all except the last line of its input.
head
is the wrong tool. If you want to see all but the last line, use:
sed \$d
The reason that
# Sample of incorrect code:
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')
fails is that wc
consumes all of the input and there is nothing left for head
to see. wc
inherits its stdin from the subshell in which it is running, which is reading from the output of the echo
. Once it consumes the input, it returns and then head
tries to read the data...but it is all gone. If you want to read the input twice, the data will have to be saved somewhere.
Using sed:
sed '$d' filename
will delete the last line of the file.
$ seq 1 10 | sed '$d'
1
2
3
4
5
6
7
8
9
For Mac OS X specifically, I found an answer from a comment to this Q&A.
Assuming you are using Homebrew, run brew install coreutils
then use the ghead
command:
cat myfile.txt | ghead -n -1
Or, equivalently:
ghead -n -1 myfile.txt
Lastly, see brew info coreutils
if you'd like to use the commands without the g
prefix (e.g., head
instead of ghead
).
cat myfile.txt | echo $(($(wc -l)-1))
This works. It's overly complicated: you could just write echo $(($(wc -l)-1)) <myfile.txt
or echo $(($(wc -l <myfile.txt)-1))
. The problem is the way you're using it.
cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')
wc
consumes all the input as it's counting the lines. So there is no data left to read in the pipe by the time head
is started.
If your input comes from a file, you can redirect both wc
and head
from that file.
head -n $(($(wc -l <myfile.txt) - 1)) <myfile.txt
If your data may come from a pipe, you need to duplicate it. The usual tool to duplicate a stream is tee
, but that isn't enough here, because the two outputs from tee
are produced at the same rate, whereas here wc
needs to fully consume its output before head
can start. So instead, you'll need to use a single tool that can detect the last line, which is a more efficient approach anyway.
Conveniently, sed offers a way of matching the last line. Either printing all lines but the last, or suppressing the last output line, will work:
sed -n '$! p'
sed '$ d'