이름이 패턴과 일치하고 내용이 패턴과 일치하는 모든 파일에 대해 명령을 실행하는 방법은 무엇입니까?

이름이 패턴과 일치하고 내용이 패턴과 일치하는 모든 파일에 대해 명령을 실행하는 방법은 무엇입니까?

단어가 포함된 cmd모든 *.cpp및 파일 에 대해 실행하고 싶다고 가정해 보겠습니다.*.hppFOO

을 텐데발견하다파일이 사라져요. 할 수 있다는 걸 알아요.

find /path/to/dir -name '*.[hc]pp' -exec grep -l 'FOO' {} +

하지만 cmd내가 할 수 있도록 처리를 확장하는 올바른 방법은 무엇입니까(예: 각 파일에 대해)?

-exec bash -c '...'나는 내가 할 수 있고 쓸 수 있다는 것을 안다 ." 내용에 다음이 포함되어 FOO있으면 cmd파일에서 실행합니다 . "논리 ...인데 대포를 쏘는 것 같은 느낌이 든다.

답변1

-exec … \;또한 내부 명령이 종료 상태 0을 반환하는 한 테스트는 성공합니다. -exec … +하나의 명령으로 많은 경로 이름을 처리할 수 있으며 항상 성공하므로 이는 유용한 테스트가 아닙니다.

grep -q-exec … \;일치하는 항목이 있으면(오류가 감지된 경우에도) 종료 상태 0을 반환하고 그렇지 않으면 1을 반환하므로 함께 잘 작동합니다 .

-exec grep … +필요한 명령을 조건부로 실행하기 위해 다른 파일을 추가할 수 있도록 파일을 하나씩 테스트합니다 .-exec grep -q … \;-exec

find /path/to/dir -name '*.[hc]pp' -exec grep -q 'FOO' {} \; -exec …

일반적으로 -exec … \;테스트를 사용하면 거의 모든 것을 테스트할 수 있는 사용자 정의 테스트를 구축할 수 있습니다. 특히 sh -c파이프, 조작할 수 있는 변수, 셸 조건 등을 사용하여 테스트를 실행할 수 있기 때문입니다. 조금:find -exec sh -c사용해도 안전한 가요 ?).

답변2

이러한 유틸리티(및 ksh 스타일 프로세스 대체를 지원하는 셸)의 GNU 구현을 사용하면 다음을 수행할 수 있습니다.

xargs -r0a <(grep -rlZ --include='*.[hc]pp' FOO) cmd --

위에서 -r(일명 --recursive) 을 사용하면 작업을 grep수행하고 (현재 버전에서는 해당 명령에 전달된 것처럼 동작함 ) NUL로 구분된 파일 경로 목록( ) 을 파이프하여 (일명 ) 에 전달된 경로를 파이프합니다 .findgrep-type ffindl-Zxargs-a--arg-file

계속 사용하고 싶다면 find(예: 파일 이름 접미사보다 더 복잡한 파일 조건 적용) 다음을 수행할 수 있습니다.

xargs -r0a <(
  find . -name '*.[hc]pp' -type f -exec grep -lZ FOO {} +
  ) cmd

( --파일 경로가 ./so not -nor 로 시작하므로 여기서는 필요하지 않습니다 +.)

실행은 find ... -exec grep -q FOO {} \; -exec cmd {} +작동하지만 프로세스를 분기하고 실행하는 것을 의미합니다. 여기 에는 각 파일에 대한 공유 라이브러리를 로드하고 연결하는 작업이 포함됩니다. 이는 여기서 수행해야 하는 작업 (몇 KiB의 텍스트를 읽고 살펴보는 것) grep보다 훨씬 더 비쌉니다 . , 따라서 성능이나 리소스 사용량이 문제라면 가능하면 피하는 것이 가장 좋습니다.grepFOO

GNU System²를 사용하지 않지만 POSIX 표준 의 다음 버전을 find지원 -print0하고 지원하는 경우 다음을 사용하여 파일 경로를 보고할 수 있습니다 .xargs-r-0perl

find . -name '*.[ch]pp' -type f -print0 |
  xargs -r0 perl -0lne 'if (/FOO/} {print $ARGV; close ARGV}' |
  xargs -r0 cmd

이는 stdin에서 읽지 않는다고 가정하지만 cmd(여기서는 구현에 따라 xargs/dev/null에서 열리거나 파이프의 읽기 끝이 perl에서 상속되어 쓰기 중입니다 xargs).


<(...)1 rc와 유사한 쉘에 pdksh, zsh 및 bash를 기반으로 한 것 이외 의 ksh 구현을 포함합니다.<{...}(...|psub)fish

² 그러나 GNU는 grep한때 BSD에서 사용되었으며(그리고 여전히 일부 BSD에서 사용됨) GNU에서 벗어난 일부 BSD는 해당 API를 복사했기 때문에 GNU가 아닌 구현이 GNU 가 아닌 구현 을 지원한다는 grep점에 유의하세요. -표준 옵션; 때로는 OpenBSD , FreeBSD 또는 MacOS와 같은 긴 옵션과 동등한 옵션 만 있습니다 .grep-Z--null-Z

답변3

globstar먼저 Bash에서 이 옵션을 활성화하세요.

shopt -s globstar

이제 간단히 다음을 수행할 수 있습니다.

for x in /path/to/dir/**/*.[hc]pp; do
  if grep -q 'FOO' "$x"; then
    ...
  fi
fi

패턴 **은 0개 이상의 경로 구성 요소와 일치합니다(디렉토리만 뒤에 오는 경우 /).

따라서 패턴 /path/to/dir/foo.cpp도 발견됩니다 /path/to/dir/deeply/nested/foo.cpp.

답변4

xargs를 연결하는 흥미로운 사례:

find /path/to/dir -name '*.[hc]pp' -print0 | xargs -0 grep -lZ 'FOO' | xargs -0 ...

명령에 따라 -n 1다음으로 전달해야 할 수도 있습니다.두번째xargs각 파일에 대해 명령을 실행하기 위해 호출됩니다 .

find명령은 일치하는 파일 이름의 0으로 구분된 이진 목록을 생성합니다. 첫 번째 xargs명령은 모든 파일에서 일치하는 콘텐츠를 테스트하기 위해 최소 개수의 grep명령을 연결하여 패턴과 일치하는 0으로 구분된 이진 파일 목록을 생성하고, 두 번째 xargs명령은 실행됩니다 . 필요한 명령.

관련 정보