指定するキーワードを全て含むファイルを探すスクリプト
指定するキーワードのいずれかを含むファイルを探す場合には、単純に
egrep -lr "(key1|key2|key3|key4|key5|key6)" data
としてやれば良くて、結果は以下のようになる。
data/1_2_3_4_5.txt data/1_2_3_4_5_6.txt data/1_2_3_5_6.txt
ただ、その逆、「指定するキーワードの全て」を含むファイルを探すとなるとなかなか簡単にはいかない。
一案として、
grep -lr key6 $(grep -lr key5 $(grep -lr key4 $(grep -lr key3 $(grep -lr key2 $(grep -lr key1 data)))))
なんていうのも一つの手だが、キーワードが増えるごとにネストの段数が増えるのが煩わしいし、なんか気持ち悪い。
なので、ちょっとしたシェルスクリプトを書いてみた。 なお、【パスに空白が含まれたりする】ケースは知らないなので、その辺りはご容赦を。
#! /bin/bash #debug=1 dir=$1 if [ ! -d "${dir}" ]; then printf "%s: No such directory.\n" "${dir}" exit 1 fi shift keys=($@) if [ ${#keys[@]} -eq 0 ]; then printf "Usage: %s dir keyword1 [keyword2 ...]\n" "${0}" exit 2 fi if [ ${debug} ]; then echo "Count of keys is ${#keys[@]}" fi for f in $(find "${dir}" -type f) ; do ct=0 for k in ${keys[@]} ; do if [ ${debug} ]; then echo "Checking ${f} for keyword '${k}'..." grep -m 1 -c "${k}" "${f}" /dev/null | grep -v ':0$' fi tmpCt=$(grep -m 1 -c "${k}" "${f}" /dev/null | grep -v ':0$' | wc -l) if [ ${tmpCt} -eq 0 ]; then break fi ct=$((ct+tmpCt)) done if [ ${ct} -eq ${#keys[@]} ]; then if [ ${debug} ]; then echo "OK: ${f}" else echo "${f}" fi else if [ ${debug} ]; then echo "NG: ${f}" fi fi done
これを
$ debug=1 ./grep-all.sh data key1 key2 key3 key4 Count of keys is 4 Checking data/1_2_3_4_5.txt for keyword 'key1'... data/1_2_3_4_5.txt:1 Checking data/1_2_3_4_5.txt for keyword 'key2'... data/1_2_3_4_5.txt:1 Checking data/1_2_3_4_5.txt for keyword 'key3'... data/1_2_3_4_5.txt:1 Checking data/1_2_3_4_5.txt for keyword 'key4'... data/1_2_3_4_5.txt:1 OK: data/1_2_3_4_5.txt Checking data/1_2_3_4_5_6.txt for keyword 'key1'... data/1_2_3_4_5_6.txt:1 Checking data/1_2_3_4_5_6.txt for keyword 'key2'... data/1_2_3_4_5_6.txt:1 Checking data/1_2_3_4_5_6.txt for keyword 'key3'... data/1_2_3_4_5_6.txt:1 Checking data/1_2_3_4_5_6.txt for keyword 'key4'... data/1_2_3_4_5_6.txt:1 OK: data/1_2_3_4_5_6.txt Checking data/1_2_3_5_6.txt for keyword 'key1'... data/1_2_3_5_6.txt:1 Checking data/1_2_3_5_6.txt for keyword 'key2'... data/1_2_3_5_6.txt:1 Checking data/1_2_3_5_6.txt for keyword 'key3'... data/1_2_3_5_6.txt:1 Checking data/1_2_3_5_6.txt for keyword 'key4'... NG: data/1_2_3_5_6.txt Checking data/2_3_4_5_6.txt for keyword 'key1'... NG: data/2_3_4_5_6.txt
としたり、
$ ./grep-all.sh data key1 key2 key3 key4 key5 key6 data/1_2_3_4_5_6.txt
としたり、という感じで。
【2023/06/11改版】 オリジナルのスクリプトは、key1にヒットしなくてもkey2~key6をチェックしてしまい、非常に効率が悪いと気付いたので改善。
オリジナルも残しておく。
#! /bin/bash #debug=1 dir=$1 if [ ! -d "${dir}" ]; then printf "%s: No such directory.\n" "${dir}" exit 1 fi shift keys=($@) if [ ${#keys[@]} -eq 0 ]; then printf "Usage: %s dir keyword1 [keyword2 ...]\n" "${0}" exit 2 fi if [ ${debug} ]; then echo "Count of keys is ${#keys[@]}" fi for f in $(find "${dir}" -type f) ; do ct=0 for k in ${keys[@]} ; do ct=$((ct+$(grep -m 1 -c "${k}" "${f}" /dev/null | grep -v ':0$' | wc -l))) done if [ ${ct} -eq ${#keys[@]} ]; then if [ ${debug} ]; then echo "OK: ${f}" else echo "${f}" fi else if [ ${debug} ]; then echo "NG: ${f}" fi fi done