-
圧縮ファイルを grep する
- 2020年10月16日
- bash
- Papertrail
- shell
業務中に圧縮ログを検索したいという状況がたまにあると思います。
そんなとき本記事が参考になれば幸いです。
前提
知識
下記を理解していないと難しい内容かもしれません。
- シェル(
sh
)のパイプライン
想定読者
- 圧縮ファイルが展開できないほど大きいのでそのまま検索したい
- 圧縮ファイルを展開するのが面倒なのでそのまま検索したい
- 圧縮ファイル内の検索で余計なソフトをインストールしたくない
- 圧縮 tar ファイルの中とかも検索したい
モチベーション
弊社は Heroku を使ったプロダクトが多いため、 Papertrail でログを管理する管理することが多いです。
Papertrail のログは、一定期間経つと .tsv.gz
ファイルで保存されるのですが、9 ヶ月分のログを調査する必要に迫られました。
ですが、素直にログを展開しようとすると、あっというまに数十 GB を超えてしまい、慌てて gunzip
コマンド 1 を terminate (終了)させました。
やむなく圧縮ファイルのまま検索を行おうとしたのが今回のモチベーションです。
圧縮ファイルの検索
1. 素直にツールを使う
有名どころでは ripgrep を利用するのが良いと思います。
インストール方法は割愛します。こちら などをご参考ください。
ripgrep は rg
コマンドで呼び出し、-z
もしくは --search-zip
オプションで圧縮ファイル内の文字列も検索してくれます。
rg -z '検索文字列' 圧縮ファイル名 $
例として fortune コマンド2で作成したサンプルファイル fortunes.txt.gz
を利用します3。
次のコマンドでサンプルファイルを作成しました。
for i in `seq 1 100` ; do fortune ; echo "-----" ; done >> fortunes.txt
$ gzip fortunes.txt # fortunes.txt.gz が作成される $
rg
コマンドに -z
オプションをつけると、圧縮ファイルのまま検索できていることがわかります。
rg -z programming fortunes.txt.gz
$ 375: A programming language that is sort of like Pascal except more like
grep コマンドと同様のオプションも一部サポートしています。
rg -z -A 4 programming fortunes.txt.gz # マッチ行後 4 行も表示
$ 375: A programming language that is sort of like Pascal except more like
376- assembly except that it isn't very much like either one, or anything
377- else. It is either the best language available to the art today, or
378- it isn't.
379- -- Ray Simard
rg -z -C 4 programming fortunes.txt.gz # マッチ行前後 4 行を表示
$ 371------
372-Life exists for no known purpose.
373------
374-C, n:
375: A programming language that is sort of like Pascal except more like
376- assembly except that it isn't very much like either one, or anything
377- else. It is either the best language available to the art today, or
378- it isn't.
379- -- Ray Simard
2. パイプラインを駆使する
ripgrep を利用しない場合、展開後のデータを(ファイルに書き出さず)そのままパイプに渡すことで検索できます。
# gzip -d で gzip ファイルを展開できる
cat fortunes.txt.gz | gzip -d | grep -C 4 programming
$ -----
Life exists for no known purpose.
-----
C, n:
A programming language that is sort of like Pascal except more like
assembly except that it isn't very much like either one, or anything
else. It is either the best language available to the art today, or
it isn't.
-- Ray Simard
複数ファイルを一度に検索したい場合は、for を利用できます。
以下 Bash での例です。
for f in *.txt.gz ; do
$ cat "$f" |
gzip -d |
grep programming # 検索したい文字列を指定
done
# 結果
A programming language that is sort of like Pascal except more like
sed でファイル名を先頭につけるとわかりやすくなります。
for f in *.txt.gz ; do
$ cat "$f" |
gzip -d |
grep programming |
sed -e "s/^/$f /"
done
# 結果
fortunes.txt.gz: A programming language that is sort of like Pascal except more like
応用:アーカイブファイルの検索
.tar.gz
拡張子の圧縮アーカイブファイルを、一時ファイルなしで検索する場合のケースを考えます。
検証用のファイル fortunes.tar.gz
を作成します。
# 1000 回ずつ fortune コマンドの実行結果を書き出したファイルを 5 つ用意
for i in `seq 5` ; do for j in `seq 1000` ; do fortune; echo "-----" ; done >> fortunes-${i}.txt ; done
$ # 少し時間がかかります
# 圧縮アーカイブの作成
tar cvzf fortunes.tar.gz *.txt
$ fortunes-1.txt
fortunes-2.txt
fortunes-3.txt
fortunes-4.txt
fortunes-5.txt
rm *.txt
$ ls
$ fortunes.tar.gz
この場合、検索するために通常は
- tar ファイルの展開
- ファイルごとの文字列検索
という 2 ステップ必要ですが、これもパイプを利用して一時ファイルなしに行うことができます。
ファイル名等関係なく、ただ検索したい場合は、tar コマンドで -O
もしくは --to-stdout
オプションをつけて展開すると、ファイルの中身だけが標準出力に出力されます。
これをそのまま grep に渡せばよいでしょう。
tar -Ozxf fortunes.tar.gz | head -n 2 # 先頭だけ表示
$ Many a bum show has been saved by the flag.
-- George M. Cohan
ls
$ fortunes.tar.gz # ファイルは展開されない
# grep で検索
tar -Ozxf fortunes.tar.gz | grep programming
$ "After three days without programming, life becomes meaningless."
Real programmers disdain structured programming. Structured programming is
BASIC is to computer programming as QWERTY is to typing.
4. functional 4. digital 4. programming
programming task.
...
「検索にマッチした文字列がどのファイルに含まれていたか」を表示するには、一工夫が必要です。
まず、tar コマンドは -t
オプションで、アーカイブファイルのリストが標準出力に書き出されます。
tar -tzf fortunes.tar.gz
$ fortunes-1.txt
fortunes-2.txt
fortunes-3.txt
fortunes-4.txt
fortunes-5.txt
また、tar コマンドはファイル展開時、ファイル名を引数に与えると、与えられたファイルだけが展開されます。
tar -Oxzf fortunes.tar.gz fortunes-1.txt | head -n 2
$ Many a bum show has been saved by the flag.
-- George M. Cohan
tar -Oxzf fortunes.tar.gz fortunes-3.txt | head -n 2
$ Molecule, n.:
The ultimate, indivisible unit of matter. It is distinguished
これらと while 文を組み合わせて、検索文字列にファイル名を付与できます。
tar -tzf fortunes.tar.gz |
$ while read -r f ; do # $f にアーカイブされたファイル名が順に読み込まれる
tar -Oxzf fortunes.tar.gz "$f" | # ファイル $f のみ標準出力へ書き出す
grep programming | # 検索
sed -e "s/^/$f: /" # 検索文字列の先頭にファイル名を付与
done
# 結果
fortunes-1.txt: "After three days without programming, life becomes meaningless."
fortunes-1.txt: Real programmers disdain structured programming. Structured programming is
fortunes-2.txt: BASIC is to computer programming as QWERTY is to typing.
fortunes-2.txt: 4. functional 4. digital 4. programming
fortunes-2.txt: programming task.
fortunes-2.txt: Algol-60 surely must be regarded as the most important programming language
fortunes-2.txt: -- From the programming notebooks of a heretic, 1990.
fortunes-3.txt: "For the sheer *joy* of programming!" she cries triumphantly.
関数化しておくと汎用的になります。
grep_in_tar() {
# grep に渡す引数だけ抜き出す
greparg=()
while [[ -n $1 ]] ; do
if [[ $1 =~ \.tar(\.gz)? ]] ; then
break
fi
greparg+=("$1")
shift
done
for tarfile in $@ ; do
tar -tf "$tarfile" |
while read -r f ; do
tar -Oxzf "$tarfile" "$f" |
grep ${greparg[@]} |
sed -e "s/^/$f: /"
done
done
}
grep_in_tar programming fortunes.tar.gz
$ grep_in_tar -A 2 programming fortunes.tar.gz # grep のオプションを利用可能
$ grep_in_tar programming fortunes.tar.gz hoge.tar.gz # 複数の tar ファイルを利用可能 $
この関数は標準入力から tar データを受け取れないのでもう少しこだわりたいところですが、実務で使う分には十分ではないでしょうか。
以上、圧縮ファイルの grep 方法について紹介しました。
本稿が少しでも日々の業務の助けになりましたら幸いです。
この記事を書いた人 : 上田直諒
AWS bluebird css CSV docker docker compose electron ES6 es2015 Git Heroku ITコンサルティング JavaScript justinmind less MongoDB Node.js php PostgreSQL Private Space Promise React react-router reactjs Salesforce scss Selenium Builder selenium IDE Selenium WebDriver stylus TypeScript VirtualBox VisualStudioCode vue vuejs webpack システム開発プロジェクト セキュリティ ワイヤーフレーム 上流工程 卒FIT 帳票 要件定義 設計 電力小売業界