パターンファイルがあり、それをファイルディレクトリと比較したいと思います。
パターンファイルの内容は次のとおりです(正規表現でもかまいません)。
pattern-that-occurs-in-file
pattern-that-also-occurs-in-file
コンテンツがパターンと一致する場合に表示される検索ファイルの例:
unrelated content
pattern-that-occurs-in-file
more unrelated content
pattern-that-also-occurs-in-file
further unrelated content
または:
unrelated content
pattern-that-also-occurs-in-file
more unrelated content
pattern-that-occurs-in-file
further unrelated content
サンプル検索ファイルは次のとおりです。いいえ来て:
unrelated content
more unrelated content
pattern-that-occurs-in-file
further unrelated content
または:
unrelated content
pattern-that-also-occurs-in-file
more unrelated content
further unrelated content
または:
unrelated content
more unrelated content
further unrelated content
2つのパターンが表示されるファイルのリストを出力するには、grepが必要です。一致する線が見えても構いません。
単一のコマンドでこれを実行できますか?それでは、どうすればいいですか?
答え1
正確なコマンドではありませんが、次のようになります。
num_patterns=$( wc -l < patterns_file )
for file in dir/*; do
num_occurrances=$( grep -F -o -f patterns_file "$file" | sort -u | wc -l )
if (( num_patterns == num_occurrances )); then
echo "all patterns in $file"
fi
done
パターンが正規表現の場合、一致テキストはすべての一致に対して一意でない可能性があるため、この方法は機能しません。
答え2
./*.txt
関心のあるすべてのファイルが一致し、次を含むファイルを見つけたいとします。みんな~のひもファイル内./patterns
(3行以上含めることができます):
#!/bin/bash
pathnames=( ./*.txt )
while IFS= read -r pattern; do
for pathname in "${pathnames[@]}"; do
pathnames=( ${pathnames[@]:1} )
if grep -qF -e "$pattern" "$pathname"; then
pathnames+=( "$pathname" )
fi
done
done < ./patterns
printf 'Matched: %s\n' "${pathnames[@]}"
これでパターンが循環します。パターンごとに配列内のすべてのファイルをテストしますpathnames
。パターンが一致した場合は現在のパス名を配列に保持し、そうでない場合は破棄します。最後に、pathnames
すべてのパターンを含むパス名のみが含まれます。
pathnames
アレイの管理方法により、grep
より多くのファイルが削除されると、各パターンの呼び出し数が減少します。
このコマンドは、pathnames=( ${pathnames[@]:1} )
配列から最初の(現在の)パス名を削除し、pathnames+=( "$pathname" )
最後に再配置します。
このコマンドgrep -qF -e "$pattern" "$pathname"
は本物ファイル$pathname
に$pattern
。-q
make Quietを使用しgrep
、ファイルのパターンと一致したらすぐに終了するようにします。-F
正規表現の一致ではなく文字列の比較に使用します。
sh
私は名前付き配列より簡潔な構文を好むので、bash
上記のバリエーションがあります/bin/sh
(位置パラメータがpathnames
配列を置き換えます)。
#!/bin/sh
set -- ./*.txt
while IFS= read -r pattern; do
for pathname do
shift
if grep -qF -e "$pattern" "$pathname"; then
set -- "$@" "$pathname"
fi
done
done < ./patterns
printf 'Matched: %s\n' "$@"
答え3
私が正しく理解しているなら、これはオプションです(私のロジックが妥当であれば)。ここでは、パターンが各ファイルで一意であると仮定します。
grep -R < file_with_patterns . | cut -d':' -f1 | uniq -d
grep
2 つのパターンが一致する場合は、2 行を返すか、1 行だけを返すか、何も返しません。この状況を利用して、uniq -d
ファイル名の重複結果のみを表示します。
答え4
@glenn-jackmanと@schrodigerscatcuriosityの答えは正規表現に合格しませんでした(OPは正規表現も含めるように質問を修正しました)。たとえば、パターンは1.
ファイルの「1a」と「1b」と一致しますが、パターンは2.
何も一致しませんが、両方のアルゴリズムはファイルが両方のパターンと一致すると結論付けます。第二に、パターンは123
「1234」と一致しますが、12
一致するパターンのためにgrepは追加の出力を生成しません。どちらのアルゴリズムも、ファイルが2つのパターンのうちの1つだけに一致すると結論付けます。
@kusalanandaはうまく機能しますが、より効率的な解決策があるかもしれません。
files=`find ./*.txt`
while read pattern; do
files=`echo "$files" | xargs grep -l "$pattern"` || break
done < ./patterns
echo Matched: $files
このソリューションは@kusalanandaのソリューションと似ています。つまり、パターンを繰り返しながら、一致しないファイルをすべて削除します。ただし、このソリューションはxargs grep -l
ネストされたループの代わりにファイルを使用します。したがって、ファイルごとにパターンごとに1つのgrepプロセスを実行するのではなく、パターンごとに1つのgrepプロセスを実行するので、はるかに高速でなければなりません。
PS:このソリューションはファイル名のスペースを処理しませんが、@kusalanandaは処理します。ただし、このソリューションはファイル名のスペースを処理するように簡単に変更できます。ファイル名にスペースやその他の不正な文字が含まれている場合は、まず恥ずかしがり、頭を下げて2番目に変更します。
xargs
到着
tr \\n \\0 | xargs -0
これは混乱し、主な問題とは関係がないように見えるため、これを主な解決策として含めませんでした。
PPS:最大速度のために、最もまれなパターンをパターンファイルに最初に配置し、最も一般的なパターンを最後に配置し、できるだけ多くのファイルを最初に削除します。