ZetCode

Linux head and tail Commands

last modified October 23, 2026

The head command prints the beginning of a file and the tail command prints its end. They are the quickest way to look at large files such as logs and CSV exports without opening them in an editor, and they are used constantly in pipelines. tail -f also follows a file as it grows, which makes it the standard tool for watching logs.

Both commands print 10 lines by default and accept -n for a different number of lines and -c for bytes. The examples use GNU coreutils 9.1 and three sample files: app.log, an application log with 20 lines, data.csv, a CSV file with a header, and the short auth.log.

The first lines of a file

Without options, head prints the first 10 lines.

wc -l app.log
head app.log

The log has 20 lines, and head shows the first half. If a file has fewer than 10 lines, all of it is printed.

$ wc -l app.log
20 app.log
$ head app.log
2026-10-23 08:00:00 INFO  server started on port 8080
2026-10-23 08:00:13 INFO  connected to database
2026-10-23 08:00:26 INFO  GET /index.html 200
2026-10-23 08:00:39 WARN  slow response: 1.8 s
2026-10-23 08:00:52 INFO  GET /about.html 200
2026-10-23 08:01:05 ERROR database timeout after 5 s
2026-10-23 08:01:18 INFO  retrying database connection
2026-10-23 08:01:31 INFO  connected to database
2026-10-23 08:01:44 DEBUG cache hit ratio 0.82
2026-10-23 08:01:57 INFO  GET /contact.html 200

A specific number of lines

The -n option sets the number of lines.

head -n 3 data.csv
head -3 data.csv

Looking at the first lines of a CSV file shows the column names and the format of the data. The short form -3 is an older syntax that GNU head still accepts; -n 3 is the portable form.

$ head -n 3 data.csv
id,name,city,salary
1,Anna,Bratislava,2400
2,Peter,Kosice,2100
$ head -3 data.csv
id,name,city,salary
1,Anna,Bratislava,2400
2,Peter,Kosice,2100

All lines except the last ones

With a negative number, head -n prints everything except the given number of lines at the end.

head -n -2 data.csv

The last two rows (Tomas and Jana) are omitted. This form is a GNU extension and is useful for dropping trailing summary lines.

$ head -n -2 data.csv
id,name,city,salary
1,Anna,Bratislava,2400
2,Peter,Kosice,2100
3,Lucia,Zilina,2650
4,Martin,Nitra,1980
5,Eva,Presov,2300

The last lines of a file

tail prints the last 10 lines by default; -n sets the number.

tail -n 4 app.log

The most recent entries of a log are at its end, so tail is the usual first look at a log file. tail seeks to the end of a regular file instead of reading it from the beginning, so it is fast even for files of many gigabytes.

$ tail -n 4 app.log
2026-10-23 08:03:28 INFO  retrying database connection
2026-10-23 08:03:41 INFO  connected to database
2026-10-23 08:03:54 DEBUG cache hit ratio 0.82
2026-10-23 08:04:07 INFO  GET /contact.html 200

Starting from a given line

With a plus sign, tail -n +N prints everything from line N to the end.

tail -n +2 data.csv
tail -n +2 data.csv | sort -t, -k4 -n | head -n 2

tail -n +2 skips the header line of the CSV file. Removing the header before sorting keeps it from being mixed into the data. The second command lists the two lowest salaries.

$ tail -n +2 data.csv
1,Anna,Bratislava,2400
2,Peter,Kosice,2100
3,Lucia,Zilina,2650
4,Martin,Nitra,1980
5,Eva,Presov,2300
6,Tomas,Trnava,2750
7,Jana,Martin,2050
$ tail -n +2 data.csv | sort -t, -k4 -n | head -n 2
4,Martin,Nitra,1980
7,Jana,Martin,2050

Multiple files

With several files, each part is preceded by a header with the file name. The -q option suppresses the headers and -v forces them.

head -n 2 auth.log data.csv
head -q -n 1 auth.log data.csv
tail -v -n 1 auth.log

The headers have the form ==> name <==. A missing file produces an error message, the other files are still printed, and the exit status is 1.

$ head -n 2 auth.log data.csv
==> auth.log <==
auth: login jano
auth: login alice

==> data.csv <==
id,name,city,salary
1,Anna,Bratislava,2400
$ head -q -n 1 auth.log data.csv
auth: login jano
id,name,city,salary
$ tail -v -n 1 auth.log
==> auth.log <==
auth: logout jano

Printing a range of lines

Combining the two commands extracts lines from the middle of a file.

head -n 12 app.log | tail -n 3
tail -n +10 app.log | head -n 3

Both commands print lines 10 to 12. The first takes the first 12 lines and keeps the last three of them; the second starts at line 10 and keeps three lines. The second form is easier to adjust: the starting line and the count appear directly. sed -n '10,12p' app.log does the same.

$ head -n 12 app.log | tail -n 3
2026-10-23 08:01:57 INFO  GET /contact.html 200
2026-10-23 08:02:10 INFO  server started on port 8080
2026-10-23 08:02:23 INFO  connected to database
$ tail -n +10 app.log | head -n 3
2026-10-23 08:01:57 INFO  GET /contact.html 200
2026-10-23 08:02:10 INFO  server started on port 8080
2026-10-23 08:02:23 INFO  connected to database

Bytes instead of lines

The -c option counts bytes.

head -c 19 app.log; echo
tail -c 20 app.log
printf 'Žilina\n' | head -c 2 | od -c
printf 'Žilina\n' | head -c 3; echo

The first 19 bytes of the log are the timestamp of the first entry; the echo adds the missing newline. The last 20 bytes include the final newline. -c counts bytes, not characters: the letter Ž takes two bytes in UTF-8, so head -c 2 prints only that letter and head -c 3 prints Ži. Cutting in the middle of a multibyte character produces an invalid byte. Byte counts are useful for binary files, for example to read a file header.

$ head -c 19 app.log; echo
2026-10-23 08:00:00
$ tail -c 20 app.log
T /contact.html 200
$ printf 'Žilina\n' | head -c 2 | od -c
0000000 305 275
0000002
$ printf 'Žilina\n' | head -c 3; echo
Ži

Following a growing file

The -f (--follow) option keeps tail running and prints new lines as they are written to the file.

timeout 4 tail -n 2 -f app.log
echo $?

While tail was running, another process appended three lines to the log, and they appeared immediately. Normally, you stop tail -f with Ctrl+C; here, timeout stops it after four seconds and returns the status 124.

$ timeout 4 tail -n 2 -f app.log
2026-10-23 08:03:54 DEBUG cache hit ratio 0.82
2026-10-23 08:04:07 INFO  GET /contact.html 200
2026-10-23 08:04:20 INFO  GET /login 200
2026-10-23 08:04:33 INFO  POST /login 302
2026-10-23 08:04:46 INFO  GET /dashboard 200
$ echo $?
124

Following a log through rotation

tail -f follows the open file. When a log is rotated (renamed and replaced with a new, empty file), it keeps watching the old one. The -F option follows the name instead.

timeout 4 tail -F svc.log

During the run, svc.log was renamed to svc.log.1, and a new svc.log was created. tail -F reports the change and continues with the new file. -F is the same as --follow=name --retry, so it also waits for a file that does not exist yet. Use it for logs managed by logrotate.

$ timeout 4 tail -F svc.log
2026-10-23 09:00:00 INFO  service up
2026-10-23 09:00:01 INFO  request 1
tail: 'svc.log' has become inaccessible: No such file or directory
tail: 'svc.log' has appeared;  following new file
2026-10-23 09:00:02 INFO  log rotated
2026-10-23 09:00:03 INFO  request 2

Stopping when a process ends

The --pid option ends tail -f when the given process terminates.

(for i in 1 2 3; do echo "step $i" >> job.log; sleep 0.5; done) &
tail -f --pid=$! -s 0.2 job.log

The background job writes three lines, and $! holds its PID. When the job ends, tail prints the remaining output and exits by itself. The -s option sets how often tail checks the process, here every 0.2 seconds.

$ (for i in 1 2 3; do echo "step $i" >> job.log; sleep 0.5; done) &
$ tail -f --pid=$! -s 0.2 job.log
step 1
step 2
step 3

Filtering with grep

tail is often combined with grep to look only at interesting lines.

tail -n 10 app.log | grep -E 'WARN|ERROR'
timeout 2 tail -n 0 -f app.log | grep --line-buffered ERROR

The first command searches the last ten lines for warnings and errors. The second watches the log and prints only new errors; -n 0 skips the existing lines. When the output of grep goes to another pipe or a file, --line-buffered makes it pass each line on immediately instead of buffering it.

$ tail -n 10 app.log | grep -E 'WARN|ERROR'
2026-10-23 08:02:49 WARN  slow response: 1.8 s
2026-10-23 08:03:15 ERROR database timeout after 5 s
$ timeout 2 tail -n 0 -f app.log | grep --line-buffered ERROR
2026-10-23 08:05:00 ERROR disk full

head in pipelines

head limits the output of other commands, for example to show the top entries of a sorted list.

sort -t, -k4 -nr data.csv | head -n 3
ls -t | head -n 1
time (seq 100000000 | head -n 2)

The first command lists the three highest salaries, and the second prints the most recently modified file. When head has printed enough lines, it exits; the command writing into the pipe then receives the SIGPIPE signal and stops too. That is why the last pipeline finishes in milliseconds, although seq would otherwise print a hundred million numbers.

$ sort -t, -k4 -nr data.csv | head -n 3
6,Tomas,Trnava,2750
3,Lucia,Zilina,2650
1,Anna,Bratislava,2400
$ ls -t | head -n 1
app.log
$ time (seq 100000000 | head -n 2)
1
2

real	0m0.002s
user	0m0.000s
sys	0m0.004s

Best practices for head and tail

Source

GNU head manual

GNU tail manual

In this article, we have used the head and tail commands to view the beginning and end of files, extract line ranges, work with bytes, and follow growing log files.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.