Linux tail 命令:查看文件末尾与实时跟随日志

tail 用于查看文件末尾;不加选项时默认输出最后 10 行。用 -n 指定行数,用 -f 持续查看追加内容;在 GNU tail 中,文件因日志轮转被替换时,可考虑 -F 按文件名重新打开并重试。

排查故障时,可以先看最近的日志,再决定是否持续跟踪。以下说明行数与字节范围、轮转、过滤、多个文件以及权限问题。示例中的日志路径和文件名需要替换为实际存在、且自己有权读取的文件。

tail 是什么?

tail 输出输入数据的末尾部分。没有指定文件,或文件参数为 - 时,它读取标准输入;默认按行处理,显示末尾 10 行。文件不足 10 行时,输出已有内容。

它适合查看大型日志的尾部,不负责保存日志、识别故障原因或建立历史索引。持续跟踪只反映能从输入读取到的内容,不能代替日志收集和归档。

基本语法与用法

下列语法中的方括号表示可选项,options 和 file... 是占位说明,不应原样输入:

tail [options] [file...]

查看 /var/log/app.log 的最后 10 行:

tail /var/log/app.log

需要最后 20 行时,指定 -n 20:

tail -n 20 /var/log/app.log

某些实现也支持 -20 这样的旧式简写,但脚本和文档中使用明确的 -n 20 更易理解。文件不存在会报错;不要将路径示例误认为每台 Linux 都自带的日志文件。

如何输出指定范围?

最后 N 行

-n N 从文件末尾取出最多 N 行。例如,查看最近 50 行访问记录:

tail -n 50 access.log

需要分页阅读较长输出时,可以通过管道交给 less:

tail -n 500 access.log | less

在 less 中上下移动阅读,按 q 退出。这里的管道只传递末尾 500 行,不是把整个原文件交给分页器。

从第 N 行到末尾

-n +N 中的加号改变了含义:从文件开头数到第 N 行,再输出到末尾,行号从 1 开始。

tail -n +2 data.csv

对于第一行就是单行表头的 CSV,这会跳过第一行。不过 tail 按换行符处理文本,不理解 CSV 的引号和跨行字段;一行不一定等于一条 CSV 记录。

按字节输出

-c N 输出最后 N 个字节。GNU tail 支持容量后缀,例如 K 表示 1024,M 表示 1024²;kB 则表示 1000 字节:

tail -c 1K output.bin

这个例子最多输出最后 1024 字节,不是 1024 个字符。二进制数据可能含终端控制字节,实际检查时宜重定向到文件或交给适合的查看工具。-c +N 表示从第 N 字节开始;按字节截取可能切断 UTF-8 中文字符,文本通常优先按行处理。

持续查看新日志

用 -f 跟随已打开的文件

-f 先输出已有文件的末尾,再继续输出追加内容。GNU tail 默认跟随文件描述符,即程序已打开的那个文件:

tail -f /var/log/app.log

按 Ctrl+C 停止并返回 shell。这只结束读取,不删除日志,也不会停止写日志的服务。

用 -F 应对日志轮转

日志轮转(log rotation)可能将旧文件改名,再创建同名新文件。GNU -F 相当于 --follow=name --retry:按名称跟随,并在文件暂时不可访问时继续尝试打开。

tail -F /var/log/app.log

长期查看此类服务日志时,-F 往往更合适。GNU Coreutils 的 tail 官方文档源文件详细说明了两种跟随方式;本机选项仍以 tail --help 为准。

轮转方式也可能是复制后截断原文件。GNU tail 检测到文件缩小时会提示并从开头继续读取,但轮转和写入之间仍可能存在竞态;不要把终端跟踪当成保证无丢失的审计记录。

忽略已有内容,只看之后的追加

如果不需要启动时显示原有末尾 10 行,可以将行数设为 0:

tail -n 0 -f /var/log/app.log

这会跳过初始读取时已经存在的内容,随后显示追加数据。它按文件读取位置工作,不按每条日志中的时间戳筛选;若文件会被替换,可按需要将跟随方式改为 -F。

常用选项对照

选项 作用 示例或适用情况
-n N 最后 N 行 tail -n 100 app.log
-n +N 从第 N 行开始 tail -n +2 data.csv
-c N 最后 N 字节 tail -c 2K data.bin
-f 默认跟随文件描述符 同一个文件持续追加
-F 按名称跟随并重试 改名后创建新文件的日志轮转
-s N 轮询间隔为 N 秒 tail -f -s 2 app.log;inotify 模式下通常不控制内容更新间隔
--pid=PID 指定进程结束后停止跟随 与批处理任务日志配合,需同时跟随文件
-q 不显示文件名标题 需要自行处理输出来源时使用
-v 始终显示文件名标题 明确标识输出文件

Linux 上的 GNU、BusyBox 以及其他操作系统的 tail 并非完全相同,尤其要核对 -F 和 --pid。GNU tail 使用 inotify 时通常由文件事件触发读取;轮询模式才按睡眠间隔检查。配合 --pid 时,-s 还影响进程存活检查的频率。

日志排查的实用组合

查找最近的错误

先取最后 200 行,再查找包含 ERROR 的行:

tail -n 200 app.log | grep 'ERROR'

这只是“最后 200 行中的错误”,不是“最后 200 条错误”。grep -i 可忽略大小写;grep -n 标出的行号针对其收到的管道输入,不是原日志中的绝对行号。未匹配到内容时,grep 通常以状态 1 退出,并不等于读取失败。

持续过滤错误行

需要跟随轮转后的文件,同时仅显示匹配行时,可以使用:

tail -F app.log | grep --line-buffered 'ERROR'

GNU grep 的 --line-buffered 使用逐行输出,减少管道中的缓冲延迟,但不会消除写入程序自身的缓冲。此命令仍会先处理文件原有的末尾内容;如果只要之后的追加,可同时使用 -n 0。

这只过滤屏幕输出,不修改原日志。按 Ctrl+C 停止管道;需要复杂条件、结构化字段或长期查询时,应考虑专门的日志系统。

同时查看多个文件

指定多个文件时,tail 用文件名标题区分来源:

tail -F access.log error.log

用 -v 始终显示标题,用 -q 隐藏标题。输出按读取到的顺序交错,不会自动按日志时间戳合并排序;隐藏标题也可能丢失来源信息。

写入进程结束后退出

GNU tail 的 --pid 可在指定写入进程不再存在后结束跟随。下例的 some_command 是占位符,必须替换为实际命令;重定向 > 会覆盖已有 build.log,需选用可覆盖的新日志路径。

some_command > build.log 2>&1 &
tail --pid=$! -f build.log

$! 是当前 shell 最近启动的后台任务 ID,因此应在同一个 shell 中紧接着执行第二行,不要插入另一个后台任务。写入进程与 tail 应在同一台机器上,并具有一致可见的进程 ID;若命令派生子进程后提前退出,父进程 ID 不代表所有写入都已结束。

退出检测可能稍有延迟。tail 的退出状态也不能代替后台任务的成功与否;自动化时应及时保存该任务 ID,再用 shell 的 wait 获取实际任务退出状态,并检查日志。不要只凭跟踪结束就认定任务成功。

-f 和 -F 应该怎样选择?

比较项 -f -F
跟随依据 默认是已打开的文件描述符 指定的文件名
文件被替换 可能仍读取原来打开的文件 尝试打开同名新文件
路径暂时消失 不会自动改为跟随同名新文件;原描述符仍可能可读 通过重试等待路径重新可用
适用情况 观察不被替换的文件,或特意跟随旧文件 按名称跟随轮转日志

-F 并非总是更好。若写入进程在改名后继续写旧文件,-f 可能更符合观察目的。应根据服务实际写入方式和轮转策略选择,并核对路径和权限。

哪些情况应使用其他工具?

  • systemd journal:日志保存在二进制 journal 中时,应使用 journalctl -f;指定服务可用 journalctl -f -u service_name,并替换服务名。可参照journalctl 官方手册。
  • 查看开头:使用 head 更直接。
  • 在历史内容与新内容间切换:less +F filename 可进入跟随模式;按 Ctrl+C 暂停,移动阅读后按 F 恢复跟随,按 q 退出。具体按键以本机 less 版本为准。
  • 定期重跑命令:如果要刷新的是命令结果,而不是文件追加内容,可使用英文 watch 命令说明中的方法。
  • 跨服务器长期分析:需要搜索、汇总和告警时,应使用集中日志或可观测性系统。

权限与运行环境的注意事项

Permission denied

没有读取权限时会出现 Permission denied。先通过 ls -l filename 检查文件所有者和权限,也要考虑上层目录的访问权限及安全策略;根据实际职责申请必要的读取权限,避免随意扩大日志权限或给所有命令都加 sudo。

敏感日志内容

日志可能包含认证令牌、会话标识和个人信息。共享终端、复制错误消息或向外提供日志时,只取必要范围,并遮盖敏感字段。tail 不会自动匿名化或过滤秘密信息。

常见问题

tail 默认输出多少行?

默认最多输出最后 10 行。可用 tail -n 30 filename 指定其他行数;不足该行数时输出已有内容。

如何退出 tail -f?

按 Ctrl+C 停止当前跟随并返回 shell。这不会删除或修改原日志文件,也不会停止日志服务。

为什么轮转后看不到新内容?

-f 可能仍跟随旧文件。可尝试 GNU tail -F filename 按名称重新打开;仍无输出时,检查日志路径、服务写入目标、权限和缓冲情况。

能直接读取压缩日志吗?

tail 不会自动解压 gzip。可用 zcat archive.log.gz | tail -n 50 解压后取末尾,但通常仍需读取解压流前面的数据,大文件可能耗时;这也不是对压缩文件直接进行实时跟随。

使用时的要点

快速查看末尾用 tail filename,限制行数用 tail -n N filename。持续读取追加内容用 -f;文件按名称轮转时,考虑 GNU -F。结合 grep 时,要区分最近的行数、匹配结果、输出缓冲和文件来源;读取或分享日志前也应核对权限与敏感信息。