はじめに
この実験では、テキストファイルの冒頭をプレビューするための強力なユーティリティである 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 メソッド、リクエストされたリソース、ステータスコード、レスポンスサイズが表示されています。
表示する行数をカスタマイズする
状況によっては、10 行では多すぎたり少なすぎたりすることがあります。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 番目のフィールドを選択します。|:cutコマンドの出力をheadコマンドに渡します(パイプ)。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" を含む行を検索します。|:grepの結果をheadコマンドに渡します。head -n 3: 渡された結果の最初の 3 行だけを表示します。
この組み合わせにより、ログファイル内の関連するエントリに素早く絞り込むことができ、調査をより的確かつ効率的に行うことができます。
head で巨大なファイルを探索する
調査の過程では、非常に巨大なログファイルに遭遇することがよくあります。head コマンドは、ファイルの内容すべてをメモリに読み込むことなく、冒頭部分を素早く確認するのに非常に適しています。
今使っている access.log を、実際よりもはるかに大きなファイルであると仮定してシミュレーションしてみましょう。
まず、ファイルサイズを確認します。
ls -lh access.log
次に、head を使って最初の 15 行を表示します。
head -n 15 access.log
以前見たものと同様の出力が、10 行ではなく 15 行分表示されるはずです。
巨大なファイルを扱う際に head コマンドが特に価値を発揮する理由は以下の通りです。
- 高速である:
headはファイルの先頭部分しか読み込まないため、たとえ数ギガバイトあるような巨大なファイルであっても瞬時に完了します。 - メモリ効率が良い: ファイル全体をメモリにロードする必要がありません。
- 素早いプレビュー: ファイル全体を開く手間をかけずに、ファイルの構造や内容の傾向を把握できます。
まとめ
この実験では、ファイルの冒頭を素早く確認するための強力なツールである head コマンドについて学習しました。学んだ内容は以下の通りです。
headを使用して、デフォルトでファイルの最初の 10 行を表示する方法。-nオプションを使用して、表示する行数をカスタマイズする方法。headで複数のファイルを同時に確認する方法。- パイプを使用して、
headをcutやgrepなどの他のコマンドと組み合わせる方法。 - 巨大なファイルを効率的に扱うために
headを活用する方法。
この実験では触れませんでしたが、他にも以下のような head のパラメータがあります。
-c: 行数ではなく、ファイルの先頭から指定したバイト数を表示します。-q: 複数ファイルを指定した際、ファイル名のヘッダーを表示しないようにします。-v: 常にファイル名のヘッダーを表示するようにします。



