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
- Look before processing: Use
headto check the format of a file before writing a script for it. - Use -n +2 to skip headers: Remove the CSV header before sorting or summing the data.
- Use -F for logs: It survives log rotation, unlike
-f. - Remember that -c counts bytes: Use it for binary data, not for cutting UTF-8 text.
- Use --line-buffered: Add it to
grepin pipelines that follow a log.
Source
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
List all Linux tutorials.