Linux: テキスト検索 — grepと正規表現
数千のファイルの中からあの1行を見つける — それがgrepの得意分野です。おそらくLinuxで最も頻繁に使われるコマンドでしょう。
📋 前提条件:まず以下を完了していること
- レッスン13:パイプとフィルター
1. このレッスンで学ぶこと
- grepの基本検索とよく使うオプション
- grep -rによる再帰的コード検索
- 基本正規表現
- 拡張正規表現
- 実践的なログ分析
2. 100ファイルの中からコードを見つけるストーリー
(1) 悩み:API名を変更したいが、どのファイルが使っているか不明
小明はPythonファイル内のrequests.getの呼び出しをすべてhttpx.getに変更する必要がありました。プロジェクトには100以上のファイルがあり — 1つずつ開くのは不可能でした。
(2) grep再帰検索
grep -rn "requests\.get" --include="*.py" src/
出力:
src/api/client.py:15: response = requests.get(url, headers=headers)
src/api/users.py:42: data = requests.get(f"{API_URL}/users")
src/api/orders.py:78: result = requests.get(ORDER_URL)
3ファイルに呼び出しがあり — 1秒未満で発見。
(3) 得られるもの:干し草の山から針を見つける正確さへ
小明は習慣を身につけました:コードを変更する前にgrep -rnで関連するすべての場所を確認し、確認してから編集を開始する。
3. 知識ポイント
(1) grepの基本的な使い方
# Basic search
grep "pattern" filename
# Common option combinations
grep -rn "TODO" src/ # Recursive search, show line numbers
grep -i "error" log.txt # Ignore case
grep -v "debug" log.txt # Invert match (lines NOT containing debug)
grep -c "failed" log.txt # Count only
grep -l "main" *.py # Show only matching filenames
grep -A5 -B5 "exception" log.txt # Show 5 lines before and after match
| オプション | 意味 |
|---|---|
-i |
大文字小文字を無視 |
-v |
反転一致(非一致行を表示) |
-c |
一致行数のみ出力 |
-n |
行番号を表示 |
-rまたは-R |
サブディレクトリを再帰検索 |
-l |
一致を含むファイル名のみ表示 |
-L |
一致を含まないファイル名のみ表示 |
-w |
単語全体で一致 |
-A NUM |
一致行の後NUM行を表示 |
-B NUM |
一致行の前NUM行を表示 |
-C NUM |
一致行の前後NUM行を表示 |
(2) 基本正規表現
| メタ文字 | 意味 | 例 |
|---|---|---|
. |
任意の1文字に一致 | h.tはhit / hot / hatに一致 |
^ |
行の先頭 | ^rootはrootで始まる行に一致 |
$ |
行の末尾 | bash$はbashで終わる行に一致 |
* |
前の文字の0回以上の繰り返し | ab*cはac / abc / abbcに一致 |
[abc] |
セット内の任意の1文字 | [Hh]elloはHelloまたはhelloに一致 |
[^abc] |
セットに含まれない文字 | [^0-9]は数字以外の文字に一致 |
\< |
単語の先頭 | \<rootは単語の先頭のrootに一致 |
\> |
単語の末尾 | root\>は単語の末尾のrootに一致 |
.は任意の1文字に一致します — リテラルのドット(IPアドレスなど)に一致させるにはバックスラッシュでエスケープする必要があります:grep "192\.168\." fileとし、grep "192.168." fileとしてはいけません(後者は192X168Yなどにも誤って一致します)。
(3) 拡張正規表現(grep -E)
基本正規表現では+、?、|、{}、()はエスケープが必要です。拡張正規表現ではこれらのメタ文字を直接使用できます(grep -Eまたはegrep経由):
ℹ️ 注意:基本と拡張正規表現の主な違いは、拡張正規表現(
grep -E)では+、?、|、{}、()をバックスラッシュエスケープなしで使用できる点です。基本正規表現ではgrep "error\|warning"のように|をエスケープする必要がありますが、grep -E "error|warning"では不要です。より読みやすいgrep -Eを推奨します。
| メタ文字 | 意味 | 例 |
|---|---|---|
+ |
前の文字の1回以上の繰り返し | ab+cはabc / abbcに一致、acには不一致 |
? |
前の文字が0回または1回 | colou?rはcolorまたはcolourに一致 |
| ` | ` | または |
{n,m} |
n回からm回の繰り返し | [0-9]{3,5}は3〜5桁の数字に一致 |
() |
グループ化 | `(error |
(4) ▶ サンプル:基本検索
# Search in a file
grep "root" /etc/passwd
# root:x:0:0:root:/root:/bin/bash
# Case-insensitive
echo -e "Error\nerror\nERROR" | grep -i "error"
# All three lines match
# Count
grep -c "nologin" /etc/passwd
# Output: 16
# Show line numbers
grep -n "www-data" /etc/passwd
# 33:www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
出力:
TEXTroot:x:0:0:root:/root:/bin/bash Error error ERROR 16 33:www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
(5) ▶ サンプル:反転一致
# Exclude comment lines
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$"
# Exclude debug logs
grep -v "DEBUG" app.log | grep "ERROR"
# Exclude files with certain extensions
ls -la | grep -v "\.md$"
出力:
TEXTPort 22 PermitRootLogin no PasswordAuthentication no 2026-07-07 10:23:45 ERROR Database connection failed 2026-07-07 10:24:12 ERROR Timeout waiting for response drwxr-xr-x 2 alice alice 4096 Jul 7 10:00 src -rw-r--r-- 1 alice alice 1234 Jul 7 10:00 config.yaml
(6) ▶ サンプル:再帰的コード検索
# Search for all TODO comments in the project
grep -rn "TODO\|FIXME\|HACK" --include="*.py" src/
# Search in specific file types
grep -rn "def " --include="*.py" --include="*.js" .
# Search excluding node_modules
grep -rn "api_key" --exclude-dir=node_modules --exclude-dir=.git .
# Show only filenames
grep -rl "class User" src/
出力:
TEXTsrc/api/client.py:23:# TODO: Add retry logic src/models/user.py:45:# FIXME: Handle edge case src/utils/helpers.py:12:# HACK: Temporary workaround src/api/client.py:10:def get_user(user_id): src/models/user.py:5:def validate_email(email): config/settings.py:3:api_key = "sk-xxx" src/models/user.py src/auth.py
grep -rnはコード検索のゴールデンコンビ — -rでディレクトリを再帰検索、-nで行番号表示、--includeでファイルタイプを制限。大規模プロジェクトでもgrep -rn "pattern" --include="*.py" src/で1秒以内にターゲットコードを特定できます。
(7) ▶ サンプル:コンテキスト表示
# Show 3 lines before and after the match
grep -C 3 "NullPointerException" app.log
# Show 5 lines after the match
grep -A 5 "Traceback" python_error.log
# Show 2 lines before the match
grep -B 2 "500" access.log
出力:
TEXT10:23:01 INFO Processing request 10:23:02 WARN Memory usage high 10:23:03 ERROR NullPointerException at UserService.java:45 10:23:04 INFO Retrying operation 10:23:05 WARN Connection pool low Traceback (most recent call last): File "app.py", line 42, in handle_request result = process(data) File "processor.py", line 15, in process return transform(input) ValueError: Invalid input data 192.168.1.100 - - [07/Jul/2026:10:23:44] "POST /api/data" 192.168.1.100 - - [07/Jul/2026:10:23:45] "POST /api/data HTTP/1.1" 500 0
(8) ▶ サンプル:正規表現の実践
# Match email addresses
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" contacts.txt
# Match IP addresses
grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log
# Match URLs
grep -E "https?://[^ ]+" index.html
# Match dates (YYYY-MM-DD)
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" logs/*.log
# Match lines starting with a digit and ending with a letter
grep "^[0-9].*[a-zA-Z]$" file.txt
出力:
TEXTalice@example.com bob.smith@company.org contact@my-site.net 192.168.1.100 - - [07/Jul/2026] ... 10.0.0.45 - - [07/Jul/2026] ... https://example.com/api/users http://internal.server.local/status 2026-07-07 2026-07-08 1abc 23x
(9) ▶ 総合例:ログエラー分析
#!/bin/bash
# analyze-errors.sh - Log error analysis
LOG_FILE="${1:-/var/log/syslog}"
echo "=== Error Analysis Report ==="
echo "Source file: $LOG_FILE"
echo ""
echo "1. Error count by type:"
grep -i "error" "$LOG_FILE" | awk '{print $5}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "2. Last 5 errors:"
grep -i "error" "$LOG_FILE" | tail -5
echo ""
echo "3. Critical alerts (OOM / Disk Full / Connection Refused):"
grep -iE "oom|out of memory|disk full|no space left|connection refused" "$LOG_FILE"
echo ""
echo "4. Error trend (by hour):"
grep -i "error" "$LOG_FILE" | grep -oP "\d{2}:\d{2}" | cut -d: -f1 | sort | uniq -c | sort -rn
❓ よくある質問
Q: grepと正規表現の違いは? A: grepはファイル内のパターンに一致する行を検索するコマンドラインツール、正規表現(regex)はテキストパターンを記述する構文です。grepは検索パターンとして正規表現をサポートしますが、正規表現自体はsed、awk、Pythonなど多くのツールや言語でも使える構文に過ぎません。
Q:
grep -rとgrep -Rは同じですか? A: 基本的には同じです。-Rはシンボリックリンクをたどりますが、-rはデフォルトでたどりません。日常使用ではどちらでも構いません。
Q: 大文字小文字を無視して検索するには? A:
-iオプションを使用:grep -ir "error" /var/log/。
Q: grepで中国語が検索できないのはなぜですか? A: ロケールの問題の可能性があります。中国語文字はUTF-8でマルチバイトエンコーディングされ、
grepはデフォルトでバイト単位で一致させます。export LANG=zh_CN.UTF-8またはgrep -P(Perl正規表現モード)を試してください。
Q:
grep -vの用途は? A: 反転一致 — パターンに一致しない行を出力します。他のgrep呼び出しと組み合わせて特定の行を「除外」できます。例:grep -v "^#"でコメント行を除外、grep -v "^$"で空行を除外。
📖 まとめ
grep "pattern" fileでファイルを検索、-rでディレクトリ再帰、-nで行番号表示- 基本正規表現:
.任意の文字、^行頭、$行末、*繰り返し、[abc]文字セット - 拡張正規表現(
grep -E):+1回以上、?0回または1回、|または、()グループ化 -v反転、-i大文字小文字無視、-cカウント、-lファイル名のみ表示-A/-B/-Cでコンテキスト行を表示
📝 練習問題
- 基本(難易度 ⭐):
/etc/passwdでログインシェルが/bin/bashのユーザーを検索し、ログファイルのエラーを検索して-cでカウントする - 中級(難易度 ⭐⭐):プロジェクトディレクトリを再帰検索してすべてのFIXMEとTODOコメントを見つけ、
grep -vで.gitディレクトリを除外し、総コード行数をカウントする - 上級(難易度 ⭐⭐⭐):正規表現でログファイル内の全IPアドレスに一致させ、
sortとuniq -cを組み合わせてIP別出現回数をカウントし、最頻出IPトップ10を見つける