文字列を含み、他の文字列を含まないファイルを見つける

文字列を含み、他の文字列を含まないファイルを見つける

いくつかのファイルがあり、文字列は含まれていますが、他の文字列は含まれていないファイルを探したいとします。

grepはラインベースなので、このような条件はgrep -q printf file && grep -vq '#include <stdio.h>' file機能しません。

どうすればいいですか?

(私はDebianを使用しているので、ツールのGNUバージョンへの答えが適しています。)

答え1

grep -vlパターンに一致する行が1つ以上あるファイル名を報告します。ここでは、パターンに一致する行のないファイルが必要です。 GNU grep(Debianにある)には、次の-Lオプションがあります。

grep -rlZ printf . | xargs -r0 grep -FL '#include <stdio.h>'

POSIXの場合、grep以下を無効にできますgrep -q

find . -type f -exec grep -q printf {} \; \
               ! -exec grep -Fq '#include <stdio.h>' {} \; \
               -print

grep各一般ファイルで 1 つまたは 2 つのインスタンスを実行するという意味なので、効率がはるかに低下します。

答え2

スクリプトの代わりに組み合わせてfind使用​​してください。bash -cファイルパスを取得してfile変数に保存し、さらに別のコマンドに渡します。まず、grep -q希望の単語/パターンがあることを確認してください。終了状態を使用して&&2番目の状態に渡しますgrep -q。コマンドが一致するものが見つからないと文字列が見つからないため、終了ステータスを使用してそれをechovia||演算子に渡します。

以下の例では、単語のみがfile2.txt含まれており、abra含まれていません。cadabra

$ find -type f -exec bash -c 'file="$@";grep -q "abra" "$file"  &&  grep -q "cadabra" "$file" || echo "$file" ' sh "{}" >
./file2.txt
$ ls                                                                                                                     
file1.txt  file2.txt  file 3.txt
$ cat file1.txt
abra cadabra
$ cat file2.txt                                                                                                          
abra
$ cat file\ 3.txt                                                                                                        
abra cadabra

答え3

これはとても簡単です。

for fname in ./*.c; do
  if grep -q -F "printf" "$fname" && ! grep -q -F "#include <stdio.h>" "$fname"; then
     printf 'File "%s" needs to include stdio.h\n' "$fname"
  fi
done

その後、現在のディレクトリ内のすべてのCソースファイルを調べ、ヘッダーを使用しているがprintf()含まれていないすべてのファイルを報告しますstdio.h

ただし、ヘッダーを間接的に含めることができるため、誤検出を防ぐためにできるコードをCプリプロセッサに渡し、前処理された出力でヘッダを見つけます(これはおよびgccで動作するようですclang)。

for fname in ./*.c; do
  if grep -q -F "printf" "$fname" && cc -E "$fname" | ! grep -q "^#.*stdio\.h\""; then
     printf 'File "%s" needs to include stdio.h\n' "$fname"
  fi
done

答え4

要件を正しく読み取るには、すべてのファイル一致から$PAT_INCLファイル一致を引いた値が必要です$PAT_EXCL

概念的には、これは集合減算にすぎません。 Unixには、セット操作のための非常に良い標準ユーティリティはありませんが、comm使用できます。

comm -23 <(grep --files-with-match "$PAT_INCL"  * | sort) \
         <(grep --files-with-match "$PATH_EXCL" * | sort)

2番目のgrepで一致するファイルのみを見つけると、効率が向上する可能性があります。

# Assuming filenames without whitespace
grep --files-with-match "$PAT_INCL" * | sort > incl_files
grep --files-with-match "$PAT_EXCL" $(cat incl_files) | sort > excl_files
comm -23 incl_files excl_files

関連情報