문자열을 포함하고 다른 문자열을 포함하지 않는 파일 찾기

문자열을 포함하고 다른 문자열을 포함하지 않는 파일 찾기

파일이 몇 개 있고 문자열은 포함하지만 다른 문자열은 포함하지 않는 파일을 찾고 싶다고 가정해 보겠습니다.

grep은 라인 기반이므로 이와 같은 조건은 grep -q printf file && grep -vq '#include <stdio.h>' file작동하지 않습니다.

어떻게 해야 하나요?

(저는 Debian을 사용하므로 도구의 GNU 버전에 대한 답변이 적합합니다.)

답변1

grep -vl패턴과 일치하는 행이 하나 이상 있는 파일 이름을 보고합니다. 여기서는 패턴과 일치하는 줄이 없는 파일을 원합니다. 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각 일반 파일에서 하나 또는 두 개의 인스턴스를 실행한다는 의미이므로 효율성이 훨씬 떨어집니다 .

답변2

스크립팅 대신 조합 find하여 사용하세요 . bash -c파일 경로를 가져와 file변수에 저장하고 추가로 다른 명령에 전달합니다. 먼저 grep -q원하는 단어/패턴이 있는지 확인합니다. 종료 상태를 사용하여 &&두 번째 상태로 전달합니다 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)

두 번째 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

관련 정보