tail
Show the last part of a file, or follow it live
tail prints the last 10 lines of each file it is given, or of standard input. -n changes the
count, -c counts bytes, and several files each get a ==> filename <== header that -q
suppresses.
That much mirrors head. Two things do not.
A + in the count changes what it counts from. tail -n 5 means the last five lines;
tail -n +5 means from line 5 to the end. It is the one flag people reliably get backwards, and
tail -n +2 is the standard way to drop a header row before feeding a file to something else.
-c +N does the same by byte offset, counting from 1 rather than 0.
-f does not exit. Instead of stopping at the current end of the file, tail -f blocks and
prints new lines as they are appended, which is the usual way to
watch a log in real time. -n0 -f skips the existing
content and shows only what arrives from now on. Since the process never ends on its own, --pid
exists to make it stop when some other process does.
Following has one failure that is worth understanding before you rely on it. tail -f follows the
file it opened, identified by its inode, not the name. When logrotate renames app.log to
app.log.1 and creates a fresh app.log, -f carries on watching the renamed file, which nothing
is writing to any more. It does not error, and it does not exit; it simply goes quiet, and the
quiet looks exactly like a service that has stopped logging. -F is shorthand for
--follow=name --retry, which watches the name, notices that the file behind it has been
replaced, and says so before continuing with the new one. For anything under logrotate, which on
Debian is most of /var/log, -F is the flag you want.
Sample files used on this page
Every example below was run against these files. Recreate them to follow along.
report.txt 40 numbered lines, each 5 words
line 1 of the report
line 2 of the report
line 3 of the report
…
line 40 of the report
users.csv a header row plus 6 records
name,age,department
Alice,34,Engineering
Bob,29,Sales
Carol,41,Engineering
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering
access.log 10 requests in the standard combined log format
203.0.113.5 - - [13/Aug/2026:09:12:01] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:12:03] "GET /style.css HTTP/1.1" 200 231
198.51.100.7 - - [13/Aug/2026:09:14:22] "GET /index.html HTTP/1.1" 200 512
198.51.100.7 - - [13/Aug/2026:09:14:25] "GET /missing.html HTTP/1.1" 404 162
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512
192.0.2.44 - - [13/Aug/2026:09:16:03] "POST /login HTTP/1.1" 302 0
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
198.51.100.7 - - [13/Aug/2026:09:18:51] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
bytes.txt ten bytes, no trailing newline, so the -c examples have something exact to cut
abcdefghij
livelog.txt empty, and there so the -f examples have a file to open
0
The last N lines
With no flags at all, tail prints 10 lines.
Show the last 10 lines of a file
tail report.txt
The default count, and the reason tail file is the first thing to type when a log has gone wrong.
Show output
line 31 of the report
line 32 of the report
line 33 of the report
line 34 of the report
line 35 of the report
line 36 of the report
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Show the last 5 lines
tail -n 5 report.txt
-n takes the count. The portable spelling, and the one to write in a script.
Show output
line 36 of the report
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Use the older shorthand for the same thing
tail -5 report.txt
-5 means -n 5. Obsolescent in POSIX terms, universally supported, and shorter at a prompt.
Show output
line 36 of the report
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Write the count out in full
tail --lines=4 report.txt
--lines and --bytes are the long forms of -n and -c, worth the extra characters in a script someone else will read.
Show output
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Show just the last line
tail -n1 report.txt
The single most common use: the last thing a file recorded.
Show output
line 40 of the report
Write a negative count and get the same answer
tail -n -5 report.txt
-n -5 and -n 5 mean the same thing to tail. Only a + changes the direction, which is covered below.
Show output
line 36 of the report
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Read the last records of a CSV
tail -n 3 users.csv
The header is long gone by this point in the file, so what comes back is records only.
Show output
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering
Take the end of a pipeline
sort access.log | tail -2
With no filename, tail reads standard input. After a sort this is how you see the largest values rather than the smallest.
Show output
203.0.113.5 - - [13/Aug/2026:09:15:47] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
Read from a redirect rather than a named file
tail -n 3 < report.txt
The shell opens the file and tail reads its standard input, so no filename appears anywhere. See pipes and redirection.
Show output
line 38 of the report
line 39 of the report
line 40 of the report
Take the end of generated output
seq 100 | tail -4
Anything writing to standard output can be trimmed this way, with no temporary file involved.
Show output
97
98
99
100
Take the end of literal text
printf 'one\ntwo\nthree\nfour\n' | tail -2
Useful for checking what a pipeline does to a small sample before pointing it at a real file.
Show output
three
four
Ask for nothing and get nothing
tail -n 0 report.txt; echo "(nothing)"
-n 0 prints no lines. It matters mainly in combination with -f, where it means "only what happens from now on".
Show output
(nothing)
Write the last lines to a new file
tail -n3 report.txt > last3.txt && cat last3.txt
tail writes to standard output like anything else, so the shell's > does the work. The cat is only here to show the result.
Show output
line 38 of the report
line 39 of the report
line 40 of the report
Count what tail actually returned
tail -n2 report.txt | wc -l
A file shorter than the requested count returns fewer lines without complaint, so a script that depends on getting exactly N should check.
Show output
2
Counting from the start with +N
A + reverses what the number means. tail -n +5 is not "the last 5 lines" but "everything from line 5 onwards", which makes tail the standard way to skip a header.
Skip the header row of a CSV
tail -n +2 users.csv
From line 2 to the end, so the column names are dropped and every record is kept. The idiom to use before piping a CSV into anything that would choke on its header.
Show output
Alice,34,Engineering
Bob,29,Sales
Carol,41,Engineering
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering
Start from an arbitrary line
tail -n +36 report.txt
Line 36 onwards. Unlike -n 5, this counts from the start, so it gives the same answer however long the file grows.
Show output
line 36 of the report
line 37 of the report
line 38 of the report
line 39 of the report
line 40 of the report
Use the file's length to pick the starting line
wc -l report.txt; tail -n +39 report.txt
wc -l says the file has 40 lines, so +39 gives the last two. The same result as -n 2, arrived at from the other direction.
Show output
40 report.txt
line 39 of the report
line 40 of the report
Reproduce cat with tail
tail -n +1 report.txt | head -2
+1 means "from the first line", so it prints the whole file. head -2 is only here to keep this example short.
Show output
line 1 of the report
line 2 of the report
Count the records after a header
tail -n +2 users.csv | wc -l
Skipping the header counts records rather than lines, which is what almost every CSV question actually wants: six here, from a file of seven lines.
Show output
6
Extract a column from the records only
tail -n +2 users.csv | cut -d, -f1
Dropping the header first means the column name does not turn up in the middle of the data.
Show output
Alice
Bob
Carol
Dave
Erin
Frank
Sort records without disturbing the header
tail -n +2 users.csv | sort -t, -k2 -n | head -2
Skip the header, sort the rest by the second field numerically, and take the two youngest. Sorting the file directly would place the header wherever name happens to sort.
Show output
Dave,25,Marketing
Bob,29,Sales
Several files at once
Given more than one file, tail labels each with a ==> filename <== header and separates them with a blank line.
Read the end of two files together
tail -n3 report.txt users.csv
The headers are what make the combined output readable, and they appear whenever there is more than one file.
Show output
==> report.txt <==
line 38 of the report
line 39 of the report
line 40 of the report
==> users.csv <==
Dave,25,Marketing
Erin,38,Sales
Frank,31,Engineering
Read the end of three files
tail -n2 report.txt users.csv access.log
tail -n2 /var/log/*.log is the everyday form of this, for seeing what several logs last recorded.
Show output
==> report.txt <==
line 39 of the report
line 40 of the report
==> users.csv <==
Erin,38,Sales
Frank,31,Engineering
==> access.log <==
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
Suppress the headers
tail -q -n2 report.txt users.csv
-q runs the results together with nothing between them, which suits output going to another command rather than to a person.
Show output
line 39 of the report
line 40 of the report
Erin,38,Sales
Frank,31,Engineering
Force a header for a single file
tail -v -n2 report.txt
-v prints the header even for one file, so a loop over filenames produces consistently labelled output.
Show output
==> report.txt <==
line 39 of the report
line 40 of the report
See what happens when one file is missing
tail -n2 report.txt nosuchfile.txt; echo "exit: $?"
tail reads what it can, reports what it cannot on standard error, and exits 1. A script checking the status sees the failure even though real output also arrived.
Show output
==> report.txt <==
line 39 of the report
line 40 of the report
tail: cannot open 'nosuchfile.txt' for reading: No such file or directory
exit: 1
Read a file that is not there at all
tail nosuchfile.txt; echo "exit: $?"
The same error and status, with nothing to print alongside it.
Show output
tail: cannot open 'nosuchfile.txt' for reading: No such file or directory
exit: 1
Bytes instead of lines
-c counts bytes and stops mid-line without hesitation, which suits binary files and fixed-width records rather than prose. On UTF-8 text it will happily cut a character in half.
Show the last N bytes
tail -c 20 report.txt
Exactly 20 bytes from the end, which lands mid-word because the final line is 20 characters plus a newline.
Show output
ne 40 of the report
Take a fixed number of bytes from a short file
tail -c 5 bytes.txt; echo
bytes.txt has no trailing newline, so the echo supplies one and keeps the prompt on its own line.
Show output
fghij
Take the last few bytes
tail -c 3 bytes.txt; echo
Useful for checking how a file ends: whether it has a trailing newline, or which byte a truncated download stopped at.
Show output
hij
Write the byte count out in full
tail --bytes=10 report.txt
--bytes is the long form of -c, and reads better in a script than a bare number.
Show output
he report
Count bytes from the start instead
tail -c +866 report.txt
+N counts from the beginning of the file, and bytes are numbered from 1, so this starts at the 866th byte.
Show output
eport
Read the whole file by byte offset
tail -c +1 bytes.txt; echo
+1 is the first byte, so this prints everything. Byte offsets starting at 1 rather than 0 is the detail to remember here.
Show output
abcdefghij
Following a growing file
-f is what separates tail from head: instead of exiting at the current end of the file, it blocks and prints new lines as they arrive.
Follow a file in real time
tail -f livelog.txt
Blocks and prints new lines as they are appended, instead of exiting once it reaches the current end of the file. Verified with a real background writer: two lines appended after tail -f started both appeared in its output within a second, live.
Show output
first new line
second new line
Follow only new lines, skipping existing content
tail -n0 -f watch.log
-n0 starts from the current end of the file instead of printing the last 10 lines first, which is the shape you want when only what happens from now on is interesting.
Filter a live-followed log
tail -n0 -f watch.log | grep --line-buffered ERROR
grep --line-buffered is needed here, because grep normally buffers its output in blocks when piped, which would delay matching lines until the buffer filled. Verified live: an INFO line and a later INFO line were both filtered out, and only the ERROR line in between made it through, in real time.
Show output
ERROR: disk full
Stop following once a specific process exits
tail -f --pid=1234 pidlog.txt
--pid makes tail exit on its own once the given PID is no longer running, rather than following forever, which suits tailing a service's log for the duration of a script that also manages that service. Verified: tail exited by itself immediately after the watched process was killed, and did not pick up a line appended after that.
Watch what a follow prints with nothing writing to the file
timeout 1 tail -f livelog.txt; echo "exit: $?"
livelog.txt is empty and nothing appends to it, so tail -f prints nothing and keeps waiting. timeout ends it after a second, and 124 is timeout's way of saying it had to.
Show output
exit: 124
Show the existing tail before following
printf 'a\nb\nc\n' > livelog.txt && timeout 1 tail -f -n1 livelog.txt; echo "exit: $?"
-f prints the last lines first and only then waits, so a follow always starts with some context. -n1 limits that context to one line.
Show output
c
exit: 124
Skip the existing content entirely
timeout 1 tail -f -n0 report.txt; echo "exit: $?"
Against a 40-line file, -n0 -f prints nothing at all: it starts at the end and waits. This is the difference between seeing history and seeing only what happens next.
Show output
exit: 124
Change how often tail looks for new data
timeout 2 tail -s 5 -f livelog.txt; echo "exit: $?"
-s sets the sleep interval in seconds between checks. On Linux tail uses inotify and reacts immediately anyway, so this matters mainly on filesystems where inotify does not work, such as NFS.
Show output
exit: 124
Ask to retry a file that is not there
tail --retry -n1 nosuchfile.txt; echo "exit: $?"
--retry only means anything alongside -f, and tail says so rather than silently ignoring it.
Show output
tail: warning: --retry ignored; --retry is useful only when following
tail: cannot open 'nosuchfile.txt' for reading: No such file or directory
exit: 1
Log rotation, and why -f is not enough
logrotate renames the current log and creates a fresh file with the old name. -f follows the file it opened, and -F follows the name, which is the whole difference.
Plain -f keeps watching the old file after rotation
tail -f rotlog.txt
Verified: after rotlog.txt was renamed aside and a new file created with the same name, tail -f kept following the renamed file by its original inode. It printed the content from before the rotation and never saw the new file's content at all, while still running and reporting no error.
Show output
before rotation
-F notices the replacement and follows the new file
tail -F --sleep-interval=1 rotlog4.txt
Verified: -F detected that rotlog4.txt had been replaced, printed an explicit notice saying so, and picked up the new file's content. This is the correct choice for following anything that might be rotated out from under it, which on Debian is most of /var/log.
Show output
before rotation
tail: 'rotlog4.txt' has been replaced; following new file
after rotation
Everyday patterns
Where tail turns up in real pipelines, usually pointed at something that is still being written.
Pull a slice out of the middle of a file
head -n15 report.txt | tail -n3
First 15 lines, then the last 3 of those, giving lines 13 to 15. head approaches the same file from the other end.
Show output
line 13 of the report
line 14 of the report
line 15 of the report
Read the most recent requests from a log
tail -n5 access.log
The last five entries, which for a log being written to is the part anyone actually wants.
Show output
192.0.2.44 - - [13/Aug/2026:09:16:03] "POST /login HTTP/1.1" 302 0
192.0.2.44 - - [13/Aug/2026:09:16:04] "GET /dashboard HTTP/1.1" 200 4021
198.51.100.7 - - [13/Aug/2026:09:18:51] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [13/Aug/2026:09:19:10] "GET /api/status HTTP/1.1" 500 89
192.0.2.44 - - [13/Aug/2026:09:20:33] "GET /dashboard HTTP/1.1" 200 4021
Pull one field out of the last entry
tail -n1 access.log | cut -d' ' -f1
The client address from the most recent request, which is the shape of a great many monitoring one-liners.
Show output
192.0.2.44
Extract the request line from recent entries
tail -n2 access.log | cut -d'"' -f2
Splitting on the quote character gives the request line itself, without the address and timestamp around it.
Show output
GET /api/status HTTP/1.1
GET /dashboard HTTP/1.1
Take the last of a filtered set
tail -n +2 users.csv | grep Engineering | tail -1
The same command does two different jobs in one pipeline: the first tail drops the header, the last takes one record from what survived the filter.
Show output
Frank,31,Engineering
Pick a single line by number
tail -n +2 users.csv | head -3 | tail -1
Skip the header, keep three records, take the last of them, giving the third record. sed -n '4p' users.csv does the same in one command.
Show output
Carol,41,Engineering
See only the last part of the default output
tail -n 10 report.txt | head -2
Chaining the two the other way round takes the first lines of the last ten, so this gives lines 31 and 32.
Show output
line 31 of the report
line 32 of the report
Take the last records without the header
tail -n +2 users.csv | tail -2
The header is dropped first, so the two records returned are records rather than whatever the last two lines happen to be.
Show output
Erin,38,Sales
Frank,31,Engineering