Unix - Head and Tail of File

unix - head AND tail of file

You can simply:

(head; tail) < file.txt

And if you need to uses pipes for some reason then like this:

cat file.txt | (head; tail)

Note: will print duplicated lines if number of lines in file.txt is smaller than default lines of head + default lines of tail.

How does (head; tail) file work?


OS X

For OS X, you can look at the source code for head and the source code for tail to figure out some of what's going on. In the case of tail, you'll want to look at forward.c.

So, it turns out that head doesn't do anything special. It just reads its input using the stdio library, so it reads a buffer at a time and might read too much. This means cat file | (head; tail) won't work for small files where head's buffering makes it read some (or all) of the last 10 lines.

On the other hand, tail checks the type of its input file. If it's a regular file, tail seeks to the end and reads backwards until it finds enough lines to emit. This is why (head; tail) < file works on any regular file, regardless of size.

Linux

You could look at the source for head and tail on Linux too, but it's easier to just use strace, like this:

(strace -o /tmp/head.trace head; strace -o /tmp/tail.trace tail) < file

Take a look at /tmp/head.trace. You'll see that the head command tries to fill a buffer (of 8192 bytes in my test) by reading from standard input (file descriptor 0). Depending on the size of file, it may or may not fill the buffer. Anyway, let's assume that it reads 10 lines in that first read. Then, it uses lseek to back up the file descriptor to the end of the 10th line, essentially “unreading” any extra bytes it read. This works because the file descriptor is open on a normal, seekable file. So (head; tail) < file will work for any seekable file, but it won't make cat file | (head; tail) work.

On the other hand, tail does not (in my testing) seek to the end and read backwards, like it does on OS X. At least, it doesn't read all the way back to the beginning of the file.

Here's my test. Create a small, 12-line input file:

yes | head -12 | cat -n > /tmp/file

Then, try (head; tail) < /tmp/file on Linux. I get this with GNU coreutils 5.97:

     1  y
2 y
3 y
4 y
5 y
6 y
7 y
8 y
9 y
10 y
11 y
12 y

But on OS X, I get this:

     1  y
2 y
3 y
4 y
5 y
6 y
7 y
8 y
9 y
10 y
3 y
4 y
5 y
6 y
7 y
8 y
9 y
10 y
11 y
12 y

How do I use Head and Tail to print specific lines of a file

Aside from the answers given by fedorqui and Kent, you can also use a single sed command:

#!/bin/sh
filename=$1
firstline=$2
lastline=$3

# Basics of sed:
# 1. sed commands have a matching part and a command part.
# 2. The matching part matches lines, generally by number or regular expression.
# 3. The command part executes a command on that line, possibly changing its text.
#
# By default, sed will print everything in its buffer to standard output.
# The -n option turns this off, so it only prints what you tell it to.
#
# The -e option gives sed a command or set of commands (separated by semicolons).
# Below, we use two commands:
#
# ${firstline},${lastline}p
# This matches lines firstline to lastline, inclusive
# The command 'p' tells sed to print the line to standard output
#
# ${lastline}q
# This matches line ${lastline}. It tells sed to quit. This command
# is run after the print command, so sed quits after printing the last line.
#
sed -ne "${firstline},${lastline}p;${lastline}q" < ${filename}

Or, to avoid any external utilites, if you're using a recent version of bash (or zsh):

#!/bin/sh

filename=$1
firstline=$2
lastline=$3

i=0
exec <${filename} # redirect file into our stdin
while read ; do # read each line into REPLY variable
i=$(( $i + 1 )) # maintain line count

if [ "$i" -ge "${firstline}" ] ; then
if [ "$i" -gt "${lastline}" ] ; then
break
else
echo "${REPLY}"
fi
fi
done

Using combination of head and tail to display middle line of the file in Unix


head -2 myownfile | tail -1 

should do what you want

head and tail on Unix

Use head to extract the first 53 lines. Use tail to extract the last 51 lines of the result (effectively ignoring the first 2 header lines).

Using Linux commands head and tail

tail command also comes with an + option which is not present in the head command. With this option tail command prints, the data starting from the specified line number of the file instead of the end.

For command: tail +n file_name, data will start printing from line number n till the end of the file

Let's say we have file file.txt

Hello from localhost1
Hello from localhost2
Hello from localhost3
Hello from localhost4
Hello from localhost5
Hello from localhost6

If you will use tail with + option then tail will start from the specified number like below :

head -n 4 file.txt | tail -n +1
Hello from localhost1
Hello from localhost2
Hello from localhost3
Hello from localhost4

Starts from 2nd line :

head -n 4 file.txt | tail -n +2
Hello from localhost2
Hello from localhost3
Hello from localhost4

Starts from 3rd line :

head -n 4 file.txt | tail -n +3
Hello from localhost3
Hello from localhost4

Starts from 4th line :

head -n 4 file.txt | tail -n +4
Hello from localhost4

That is the reason it's giving the same output as head -n 4 file.txt | tail -n 1

+ and - both have a different meaning in tail.



Related Topics



Leave a reply



Submit