シェルスクリプトでファイルに特定の文字が含まれているかどうかを高速に判定する方法
はじめに
ファイルに特定の文字が含まれていたら処理を行う、という分岐を書くときに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
を使うのが良さそうですね。
ディスカッション
コメント一覧
まだ、コメントがありません