同じファイル内に2つのWordインスタンスが存在するファイルを検索する方法を探しています。これまで、次の方法で検索を行ってきました。
find . -exec grep -l "FIND ME" {} \;
私が遭遇している問題は、「FIND」と「ME」の間に1つのスペースがない場合、検索結果がファイルを生成しないことです。 「FIND ME」ではなく「FIND」と「ME」の両方の単語がファイルに存在する以前の検索文字列をどのように適応させるのですか?
AIXを使用しています。
GNUツール:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
あなたは標準的に行うことができます:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
しかし、これはファイルごとに最大2つのgrep
sを実行します。このように多くのgrep
sの実行を回避し、ファイル名に任意の文字を許可しながら移植性を維持するには、次のようにします。
convert_to_xargs() {
sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
{
if (NR > 1) {
printf "%s", line
if (!index($0, "//")) printf "\\"
print ""
}
line = $0
}'
END { print line }'
}
export LC_ALL=C
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
find
の出力をxargs(C
ロケールではSPC/TAB/NL、他のロケールではYMMV)で区切られた単語のリストに変換して、単一引用符、二重引用符、バックスラッシュで空白をエスケープできる形式に変換するというアイデアとお互い)。
通常、find -print
の出力を後処理することはできません。これは、ファイル名を改行文字で区切り、ファイル名に含まれる改行文字をエスケープしないためです。たとえば、次の場合:
./a
./b
a<NL>.
というディレクトリにあるb
という1つのファイルか、現在のディレクトリにあるa
とb
という2つのファイルかどうかを確認する方法がありません。
.//.
を使用することにより、//
は、find
による出力としてファイルパスに表示されません(名前が空のディレクトリはなく、/
はファイルで許可されていないため) name)、//
を含む行が見つかれば、それが新しいファイル名の最初の行であることがわかります。したがって、そのawk
コマンドを使用して、すべての改行文字をエスケープし、それらの行の前にある文字をエスケープすることができます。
上記の例の場合、最初のケースではfind
が出力されます(1つのファイル)。
.//a
./b
どのawkがエスケープするか:
.//a\
./b
つまり、xargs
はそれを1つの引数と見なします。 2番目のケース(2つのファイル):
.//a
.//b
どのawk
がそのまま残るので、xargs
は2つの引数を参照します。
ファイルが単一のディレクトリにあり、その名前にスペース、タブ、改行、*
、?
または[
文字が含まれておらず、-
で始まっていない場合.
でも、MEを含むファイルのリストを取得し、FINDも含むファイルに絞り込みます。
grep -l FIND `grep -l ME *`
awk
を使用すると、次のコマンドも実行できます。
find . -type f -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;
cx
とcy
を使用して、FIND
とME
にそれぞれ一致する行をカウントします。 END
ブロックでは、両方のカウンターが0より大きい場合、FILENAME
を出力します。
これはgnu awk
:
find . -type f -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +
または、次のように_egrep -e
_または_grep -E
_を使用します。
find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;
または
find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +
_+
_は、_-exec
_ edであるコマンドの引数として複数のファイル(パス)名を検索(サポートされている場合)に追加します。これはプロセスを節約し、見つかったファイルごとにコマンドを呼び出す_\;
_よりもはるかに高速です。
_-type f
_は、ディレクトリでのgrepを回避するために、ファイルのみに一致します。
'(ME.*FIND|FIND.*ME)'
は、「ME」の後に「FIND」が続く、または「FIND」の後に「ME」が続くすべての行に一致する正規表現です。 (シェルが特殊文字を解釈しないようにするための単一引用符)。
_-i
_をgrep
コマンドに追加して、大文字と小文字を区別しないようにします。
「FIND」が「ME」の前にある行のみを一致させるには、_'FIND.*ME'
_を使用します。
単語の間にスペース(1つ以上、ただしそれ以外)を必要とする場合:_'FIND +ME'
_
単語間にスペース(0以上、ただしそれ以外)を許可するには:_'FIND *ME'
_
正規表現を使用した組み合わせは無限であり、一度に1行ずつのみのマッチングに関心がある場合、egrepは非常に強力です。
受け入れられた答えを見ると、必要以上に複雑に見えます。 GNU find
とgrep
とxargs
のバージョンは、NULLで終了する文字列をサポートしています。これは次のように簡単です:
find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME
find
コマンドを変更して、目的のファイルにフィルタリングできます。これは、任意の文字を含むファイル名で機能します。 sed
解析の複雑さを追加する必要はありません。ファイルをさらに処理する場合は、最後のgrep
に別の--null
を追加します
find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo
そして、関数として:
find_strings() {
find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}
明らかに、これらのツールのGNUバージョンを実行していない場合は、受け入れられた回答を使用してください。