シェルスクリプトでファイルに特定の文字が含まれているかどうかを高速に判定する方法
はじめに
ファイルに特定の文字が含まれていたら処理を行う、という分岐を書くときにgrepの実行結果を>/dev/nullに捨てる、という方法をとっていましたが、>/dev/nullを使わなくても良いということを知りました。
>/dev/nullを使う場合と比べてのメリットについても取り上げます。
検証環境
$ uname -moi
x86_64 x86_64 GNU/Linux
$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
$ grep --version | head -n 1
grep (GNU grep) 2.20>/dev/nullを使ったやり方
今までは標準出力を無効化するために>/dev/nullを使っていました。
1から100まで書かれている100行のテキストファイルtest.txtについて、grepを行い分岐処理を行ってみます。
まずはテストに使用するテキストファイルを作成します。
# `seq` コマンドでファイルを作成
$ seq 1 100 >test.txt
# 確認。最初の3行を出力してみる
$ head -n 3 test.txt
1
2
3
# 確認。最後の3行を出力してみる
$ tail -n 3 test.txt
98
99
100このファイルを使ってファイル内の文字列存在チェックを行ってみます。
# ----- 99を検索 -----
# &&を使う方法
$ grep 99 test.txt >/dev/null && echo EXIST.
EXIST.
# if文を使う方法
$ if grep 99 test.txt >/dev/null; then
echo EXIST.
fi
EXIST.
# ----- 199を検索 -----
# &&を使う方法 (何も出力されない)
$ grep 199 test.txt >/dev/null && echo EXIST.
# if文を使う方法(何も出力されない)
$ if grep 199 test.txt >/dev/null; then
echo EXIST.
fi実現できました。
grep -qを使ったやり方
grepに-qオプションというものがあることを知りました。
こちらは標準出力には何も出力しませんが、実行結果をexitコードあるいは$?で取得できる仕組みになっています。
先のコードをgrep -qを使って書き直してみます。
# ----- 99を検索 -----
# &&を使う方法
$ grep -q 99 test.txt && echo EXIST.
EXIST.
# if文を使う方法
$ if grep -q 99 test.txt; then
echo EXIST.
fi
EXIST.
# ----- 199を検索 -----
# &&を使う方法 (何も出力されない)
$ grep -q 199 test.txt && echo EXIST.
# if文を使う方法(何も出力されない)
$ if grep -q 199 test.txt; then
echo EXIST.
fi速度に大きな違いがある
両方ともそれほど大きな違いが無いように見えますが、大きなデータファイルを操作するときには顕著な違いが生まれます。
1から10000000までの数字が書かれたデータファイルをgrepするケースを試してみます。
# ファイルを作成
$ seq 1 10000000 > test.txt
# >/dev/null を使うケースの検索速度
$ time grep 99 test.txt >/dev/null
real 0m0.337s
user 0m0.158s
sys 0m0.076s
# grep -q を使うケースの検索速度
$ time grep -q 99 test.txt
real 0m0.023s
user 0m0.000s
sys 0m0.019s何度か試してみましたが、grep -qのほうが早いですね。
おそらく、grep -qの方は検索結果が1件でも見つかった場合に以降の処理を中断しているため高速なのではないか?と思われます。
ひとこと
少しだけコードは短くなりますし、速度も早いということでgrep -qを使うのが良さそうですね。







ディスカッション
コメント一覧
まだ、コメントがありません