textもしくはgzipのログファイルから任意の文字列を検索する

# はじめに

先日に職場で「テキスト状態のログファイルと、gzipで圧縮されたログファイルの中から任意の文言を含むログを抽出したい」という話になりました。
特にエラーログを調査する時は、「gzipで圧縮されたログ」と「プレーンテキストのログ」の両方を確認することが多いため、その方法を記事にまとめてみました。

# 検証環境

* WSL2（Ubuntu 22.04.3 LTS）

# zgrepによる検索

* `zgrep`コマンドを使うことで、プレーンテキストとgzipのログを合わせて検索できました。
  * `grep`コマンドと同じ使い方なので、直感的で分かりやすいです。

```shell:zgrepによる検索結果
nkojima@NKOJIMA-DESKTOP:~$ zgrep "error" /var/log/dmesg*
/var/log/dmesg:[    1.211764] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
/var/log/dmesg.0:[    4.531410] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
/var/log/dmesg.1.gz:[    1.280337] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
/var/log/dmesg.2.gz:[    0.729120] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
/var/log/dmesg.3.gz:[    0.814380] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
/var/log/dmesg.4.gz:[    0.742996] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.
```

# zgrepを使わないスクリプト

* 苦手なシェルスクリプトの学習用に、`zgrep`を使わないスクリプトを作ってみました。
  * `zgrep`を使わない場合、`file`、`grep`、`zcat`などを組み合わせることになり、かなり面倒なことになりました💦
* 以下の`log_finder.sh`は、第1引数にログを探索するパス、第2引数にログ内で探す文言を指定します。
  * 例えば以下の表のような形で指定します。

||第1引数|第2引数|
|---|---|---|
|/var/log/dmesg*にマッチするファイルから、errorを含む行を探す|"/var/log/dmesg*"|"error"|
|/var/log/syslog*.gzにマッチするファイルから、warningを含む行を探す|"/var/log/syslog*.gz"|"warning"|

* ログファイルはテキストもしくはgzipで保存されていることがあるため、どちらにも対応させています。
  * `file -i {ファイルパス}`でMIMEタイプを確認してから、`cat`もしくは`zcat`コマンドでファイルを開いて中身を確認しています。
  * ここでは`grep -q {MIMEタイプの一部}`を使うことで終了ステータスコードだけを返させて、このステータスが0であれば引数のMIMEタイプであると判定しています。
* 結果の出力時には`grep -n`を使うことで、ログの行番号を合わせて出力しています。

```shell:log_finder.sh
#!/bin/sh

# 引数1: ログを探索するパス
# 引数2: ログ内で探す文言
target_path=$1
target_text=$2

echo "「${target_path}」にマッチするファイルから、「${target_text}」を含む行を抽出します..."

for file_path in $target_path;
do
    echo "target_file: $file_path"

    if echo `file -i $file_path` | grep -q "directory"; then
        echo "This is a directory."
        echo "" # 出力をファイル間で区切るための改行
        continue
    fi

    if echo `file -i $file_path` | grep -q "gzip"; then
        zcat $file_path | grep -n $target_text
    else
        cat $file_path | grep -n $target_text
    fi
    echo "" # 出力をファイル間で区切るための改行
done

echo "処理終了"
```

* 以下の実行結果では、先頭に記載した行番号は「line 〇〇」という形式で表記したかったのですが、面倒だったのでやめておきました💦

```shell:実行結果の例
nkojima@NKOJIMA-DESKTOP:~$ ./log_finder.sh "/var/log/dmesg*" "error"
「/var/log/dmesg*」にマッチするファイルから、「error」を含む行を抽出します...
target_file: /var/log/dmesg
418:[    1.211764] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

target_file: /var/log/dmesg.0
427:[    4.531410] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

target_file: /var/log/dmesg.1.gz
427:[    1.280337] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

target_file: /var/log/dmesg.2.gz
425:[    0.729120] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

target_file: /var/log/dmesg.3.gz
418:[    0.814380] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

target_file: /var/log/dmesg.4.gz
418:[    0.742996] kernel: EXT4-fs (sdc): mounted filesystem with ordered data mode. Opts: discard,errors=remount-ro,data=ordered. Quota mode: none.

処理終了
```

# まとめ

* `zgrep`コマンドはプレーンテキスト／gzipのどちらも簡単に検索できるので、非常に便利です。
* 学習目的で`zgrep`コマンドを使わずに自作のスクリプトを作ってみたものの、コードが汚くて車輪の再発明にすらなりませんでした...


# 参考URL

* [圧縮ファイルから文字列を検索するには - @IT](https://atmarkit.itmedia.co.jp/flinux/rensai/linuxtips/556searchzgrep.html)
* [Linuxコマンド入門：file コマンドの基本とファイルの種類の調べ方、オプションについてまとめてみた](https://qiita.com/free-honda/items/6dd53f687eda24c8f77e)
* [【file】Linuxでファイルの種類を取得するコマンド](https://uxmilk.jp/53517)
* [シェルで特定の文字列を含むか判定する方法]()