はじめに
この実験では、テキストファイルの先頭部分をすばやく確認できる強力な 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 サーバーへの 1 件のリクエストを表しており、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 リクエストを含む 2 番目のフィールドを選択します。| はその出力を 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
これまで見てきたものと同様の出力が、10 行ではなく 15 行表示されます。
head コマンドが高速なのは、ファイルの先頭部分だけを読み取るためです。また、ファイル全体を読み込まないので、メモリも効率的に使用します。そのため、ファイルの構造や内容をすばやく確認できます。
まとめ
この実験では、ファイルの先頭部分をすばやく確認できる強力なツール、head コマンドについて学びました。次の操作を習得しました。
headを使い、デフォルトでファイルの先頭 10 行を表示する。-nオプションで表示する行数を変更する。headで複数のファイルを同時に確認する。- パイプを使って、
headをcutやgrepなどのコマンドと組み合わせる。 headを使って大きなファイルを効率的に扱う。
この実験では扱わなかった head の追加オプション:
-c: 行ではなく、ファイルの先頭から数バイトを表示します。-q: 複数ファイルを確認するときに見出しを表示しません。-v: 複数ファイルを確認するときに常に見出しを表示します。



