简介
在本实验中,你将学习 Linux 中的 head 命令。它是一款功能强大的工具,可以快速预览文本文件的开头内容。想象一下,你是一名数字侦探,需要从大量文件中迅速查找关键信息。head 命令就像你手中的放大镜,让你无需完整打开文件,就能查看文件开头的内容。
我们将模拟这样一个场景:你正在调查一组来自繁忙 Web 服务器的日志文件。你的目标是高效检查这些日志,发现潜在问题并获取有价值的信息。通过本次实践,你将掌握 head 命令的有效用法,让文件浏览工作更加快速、高效。
了解 head 的基本用法
在本步骤中,你将检查一个简单的日志文件,了解 head 的工作方式。
首先,进入项目目录:
cd /home/labex/project
现在,对名为 access.log 的文件使用 head 命令:
head access.log
你应该会看到类似下面的输出:
192.168.1.100 - - [01/Jan/2024:00:00:01 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.101 - - [01/Jan/2024:00:00:02 +0000] "GET /style.css HTTP/1.1" 200 567
192.168.1.102 - - [01/Jan/2024:00:00:03 +0000] "GET /logo.png HTTP/1.1" 200 2345
192.168.1.103 - - [01/Jan/2024:00:00:04 +0000] "POST /login HTTP/1.1" 302 -
192.168.1.104 - - [01/Jan/2024:00:00:05 +0000] "GET /dashboard HTTP/1.1" 200 3456
192.168.1.105 - - [01/Jan/2024:00:00:06 +0000] "GET /api/user HTTP/1.1" 200 789
192.168.1.106 - - [01/Jan/2024:00:00:07 +0000] "GET /images/banner.jpg HTTP/1.1" 200 4567
192.168.1.107 - - [01/Jan/2024:00:00:08 +0000] "POST /comment HTTP/1.1" 201 -
192.168.1.108 - - [01/Jan/2024:00:00:09 +0000] "GET /search?q=linux HTTP/1.1" 200 2345
192.168.1.109 - - [01/Jan/2024:00:00:10 +0000] "GET /about HTTP/1.1" 200 1234
默认情况下,head 会显示文件开头的 10 行。日志中的每一行代表一次对 Web 服务器的请求,其中包含 IP 地址、时间戳、HTTP 方法、请求资源、状态码和响应大小等信息。
自定义显示行数
在本步骤中,你将自定义 head 显示的行数。
如果只想查看日志开头的 5 行,可以使用 -n 选项:
head -n 5 access.log
该命令应输出:
192.168.1.100 - - [01/Jan/2024:00:00:01 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.101 - - [01/Jan/2024:00:00:02 +0000] "GET /style.css HTTP/1.1" 200 567
192.168.1.102 - - [01/Jan/2024:00:00:03 +0000] "GET /logo.png HTTP/1.1" 200 2345
192.168.1.103 - - [01/Jan/2024:00:00:04 +0000] "POST /login HTTP/1.1" 302 -
192.168.1.104 - - [01/Jan/2024:00:00:05 +0000] "GET /dashboard HTTP/1.1" 200 3456
当你只想查找文件开头附近的特定信息,而不需要查看完整的 10 行内容时,这种方式尤其有用。
检查多个文件
在本步骤中,你将使用 head 同时查看多个文件的开头内容。
让我们检查 access.log 和 error.log 两个文件的开头:
head access.log error.log
你应该会看到类似下面的输出:
==> access.log <==
192.168.1.120 - - [01/Jan/2024:00:00:53 +0000] "POST /about HTTP/1.1" 200 7616
192.168.1.147 - - [01/Jan/2024:00:00:45 +0000] "GET /dashboard HTTP/1.1" 200 7348
192.168.1.138 - - [01/Jan/2024:00:00:03 +0000] "DELETE /comment HTTP/1.1" 400 8341
192.168.1.138 - - [01/Jan/2024:00:00:31 +0000] "DELETE /about HTTP/1.1" 200 3254
192.168.1.122 - - [01/Jan/2024:00:00:15 +0000] "PUT /index.html HTTP/1.1" 500 6061
192.168.1.125 - - [01/Jan/2024:00:00:09 +0000] "DELETE /logo.png HTTP/1.1" 301 4916
192.168.1.148 - - [01/Jan/2024:00:00:33 +0000] "POST /admin/dashboard HTTP/1.1" 201 5546
192.168.1.146 - - [01/Jan/2024:00:00:56 +0000] "GET /images/banner.jpg HTTP/1.1" 301 2332
192.168.1.195 - - [01/Jan/2024:00:00:12 +0000] "DELETE /dashboard HTTP/1.1" 404 6740
192.168.1.136 - - [01/Jan/2024:00:00:18 +0000] "GET /login HTTP/1.1" 200 2374
==> error.log <==
[01/Jan/2024:00:01:23 +0000] [error] [client 192.168.1.150] File does not exist: /var/www/html/missing.html
[01/Jan/2024:00:02:34 +0000] [error] [client 192.168.1.151] PHP Parse error: syntax error, unexpected ';' in /var/www/html/index.php on line 30
[01/Jan/2024:00:03:45 +0000] [warn] [client 192.168.1.152] ModSecurity: Access denied with code 403 (phase 2). Matched phrase "sql injection attempt" at REQUEST_URI. [file "/etc/modsecurity/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "50"] [id "942100"] [rev ""] [msg "SQL Injection Attack Detected via libinjection"] [data "Matched Data: SQL injection found within REQUEST_URI: /vulnerable.php?id=1'"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [hostname "example.com"] [uri "/vulnerable.php"] [unique_id "YvKp2H8AAQEAAAxxBGIAAAAC"]
[01/Jan/2024:00:04:56 +0000] [error] [client 192.168.1.153] AH01071: Got error 'PHP message: PHP Fatal error: Uncaught Error: Call to undefined function mysql_connect() in /var/www/html/db.php:15...'
[01/Jan/2024:00:05:67 +0000] [warn] [client 192.168.1.154] ModSecurity: Warning. Matched "Operator \`Ge' with parameter \`5' against variable \`TX:ANOMALY_SCORE' (Value: \`5' ) [file "/etc/modsecurity/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "57"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [hostname "example.com"] [uri "/admin"] [unique_id "YvKp2H8AAQEAAAxxBGIAAAAD"]
[01/Jan/2024:00:06:78 +0000] [error] [client 192.168.1.155] PCE: Can't open perl script "/var/www/html/cgi-bin/printenv": No such file or directory
[01/Jan/2024:00:07:89 +0000] [warn] [client 192.168.1.156] ModSecurity: Access denied with code 403 (phase 2). Matched phrase "directory traversal attempt" at ARGS:file. [file "/etc/modsecurity/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [line "75"] [id "930110"] [rev ""] [msg "Path Traversal Attack (/../)"] [data "Matched Data: ../ found within ARGS:file: ../../../etc/passwd"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [hostname "example.com"] [uri "/download.php"] [unique_id "YvKp2H8AAQEAAAxxBGIAAAAE"]
[01/Jan/2024:00:08:90 +0000] [error] [client 192.168.1.157] PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/html/memory_hog.php on line 10
[01/Jan/2024:00:09:01 +0000] [warn] [client 192.168.1.158] ModSecurity: Warning. Pattern match "(?i:(?:[\s'\"`_''\(\)]*?(?:[\d\w]+[\s'\"`_''\(\)]*?){2,}[\s'\"`_''\(\)]*?(?:having|rongjitest|select|union|where|get_lst))" at ARGS:username. [file "/etc/modsecurity/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "1126"] [id "942480"] [rev ""] [msg "SQL Injection Attack"] [data "Matched Data: union select found within ARGS:username: admin' UNION SELECT password FROM users--"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [hostname "example.com"] [uri "/login.php"] [unique_id "YvKp2H8AAQEAAAxxBGIAAAAF"]
[01/Jan/2024:00:10:12 +0000] [error] [client 192.168.1.159] AH01797: client denied by server configuration: /var/www/html/restricted/
你可以看到,head 会使用文件名标题清晰分隔不同文件的输出。当你需要快速比较多个文件的开头内容时,这一功能非常实用。
通过管道使用 head
在本步骤中,你将通过管道把 head 与另一个命令组合使用。
假设你想查看 access.log 的前 3 行,但只关注其中的时间戳和请求资源。你可以将 cut 命令与 head 结合使用:
cut -d '"' -f2 access.log | head -n 3
该命令应输出:
POST /about HTTP/1.1
GET /dashboard HTTP/1.1
DELETE /comment HTTP/1.1
其中,cut -d '"' -f2 access.log 会以引号为分隔符拆分每一行,并选择包含 HTTP 请求的第二个字段。管道符 | 会将该输出传递给 head -n 3,后者只显示其中的前 3 行。
这种组合方式可以帮助你快速聚焦日志条目中的特定部分,让排查工作更加高效。
结合 head 和 grep 进行调查
在本步骤中,你将把 head 与 grep 结合起来,在日志中搜索特定模式。
假设你收到报告,称 /admin 页面出现了异常活动。现在,你想检查访问日志中前几条包含 /admin 的记录:
grep "/admin" access.log | head -n 3
该命令应输出类似下面的内容:
192.168.1.148 - - [01/Jan/2024:00:00:33 +0000] "POST /admin/dashboard HTTP/1.1" 201 5546
192.168.1.115 - - [01/Jan/2024:00:00:22 +0000] "PUT /admin HTTP/1.1" 302 1113
192.168.1.163 - - [01/Jan/2024:00:00:56 +0000] "POST /admin/dashboard HTTP/1.1" 301 815
其中,grep "/admin" access.log 会在 access.log 中搜索包含 /admin 的行。管道符 | 会将匹配到的行传递给 head -n 3,后者只显示前 3 条匹配结果。
这种组合方式可以帮助你快速定位日志文件中的相关记录,使调查更有针对性、更高效。
使用 head 探查大型文件
在本步骤中,你将使用 head 检查大型日志文件,而不必将全部内容加载到内存中。
为了模拟处理大型文件的场景,我们继续使用之前的 access.log 文件,并假设它的实际规模要大得多。
首先,查看文件大小:
ls -lh access.log
现在,使用 head 查看开头的 15 行:
head -n 15 access.log
你应该会看到与之前类似的输出,只是这次显示的是 15 行,而不是 10 行。
head 命令之所以运行迅速,是因为它只读取文件开头的内容;同时,它也很节省内存,因为不会加载整个文件。这样,你就能快速预览文件的结构和内容。
总结
在本实验中,我们学习了 head 命令。这是一款可以快速检查文件开头内容的强大工具。你掌握了以下用法:
- 默认使用
head查看文件开头的 10 行。 - 使用
-n选项自定义显示的行数。 - 使用
head同时检查多个文件。 - 通过管道将
head与cut、grep等其他命令组合使用。 - 使用
head高效处理大型文件。
本实验未介绍的其他 head 参数:
-c:显示文件开头的若干字节,而不是若干行。-q:检查多个文件时隐藏文件头。-v:检查多个文件时始终显示文件头。



