재귀 grep 대 find / -type f -exec grep {} \; 어느 쪽이 더 효율적 / 빠른가?


70

전체 파일 시스템에서 어떤 파일이 문자열을 포함 하는지를 찾는 데 더 효과적인 것은 무엇입니까 : 재귀 grep 또는 exec 문에서 grep으로 찾기? 파일 확장자 또는 파일 이름과 일치하는 정규 표현식을 알고 있지만 -type f어느 것이 더 나은지 알면 적어도 일부 필터링을 수행 할 수 있기 때문에 찾기가 더 효율적이라고 가정합니다 . GNU grep 2.6.3; 찾기 (GNU findutils) 4.4.2

예:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;


1
수학 / 컴퓨터 과학 / 알고리즘 효율성은 의견에 근거하지 않습니다.
— Gregg Leventhal

이것을 확인하십시오. 재귀 적이지는 않지만 어느 것이 더 나은지 이해할 것입니다. unix.stackexchange.com/questions/47983/…
— Ramesh

8
@AvinashRaj 그는 의견을 요구하지 않습니다. 그는 어느 것이 "더 나은"것이 아니라 더 효율적 이고 / 또는 더 빠른지 묻고있다 . 이것은이 두 프로그램의 업무 수행 방식과 검색을 위해 정확히 제공하는 것에 따라 달라지는 하나의 구체적인 답변을 가진 완벽하게 대답 할 수있는 질문입니다.
— terdon

2
이 -exec {} +양식은 포크 수가 적으므로보다 빠릅니다 -exec {} \;. 정확히 동등한 출력을 얻으려면 옵션에 -H(또는 -h) 를 추가해야 할 수도 있습니다 grep.
— Mikel

당신은 아마 두 번째 -r옵션을 원하지 않았을 것입니다grep
— qwertzguy

답변:


85

확실하지 않습니다 :

grep -r -i 'the brown dog' /*

정말 당신이 의미하는 것입니다. 이것은 숨겨지지 않은 모든 파일과 디렉토리에서 재귀 적으로 grep을 의미합니다 /(그러나 여전히 숨겨진 파일 내부와 디렉토리 내부를 살펴보십시오).

당신이 의미한다고 가정 :

grep -r -i 'the brown dog' /

몇 가지 참고할 사항 :

  • 모든 grep구현이 지원되는 것은 아닙니다 -r. 디렉토리 트리를 탐색 할 때 일부는 디렉토리에 대한 심볼릭 링크를 따릅니다 (즉, 동일한 파일에서 여러 번 보거나 무한 루프로 실행될 수 있음). 일부는 장치 파일 내부를 /dev/zero보거나 (예를 들어 꽤 오랜 시간이 걸릴 것입니다 ) 파이프 또는 이진 파일 ... 일부는 그렇지 않습니다.
  • grep파일을 발견하자마자 파일 내부를 조사 하기 시작하는 것이 효율적 입니다. 그러나 파일을 찾는 동안 더 이상 검색 할 파일을 더 이상 찾지 않습니다 (대부분의 경우와 마찬가지로)

너의:

find / -type f -exec grep -i 'the brown dog' {} \;

파일 당 -r하나를 실행하기 때문에 (여기서 의미가없는 제거 )는 비효율적 grep입니다. ;하나의 인수 만 허용하는 명령에만 사용해야합니다. 또한 여기에서는 grep하나의 파일 만 보이 므로 파일 이름이 인쇄되지 않으므로 일치하는 위치를 알 수 없습니다.

장치 파일, 파이프, 심볼릭 링크를 보지 않고 심볼릭 링크를 따르지 않지만 여전히 잠재적으로 내부를 살펴보고 있습니다 /proc/mem.

find / -type f -exec grep -i 'the brown dog' {} +

grep가능한 적은 명령이 실행 되기 때문에 훨씬 나을 것입니다. 마지막 실행에 파일이 하나만 있지 않으면 파일 이름을 얻게됩니다. 이를 위해서는 다음을 사용하는 것이 좋습니다.

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

또는 GNU grep:

find / -type f -exec grep -Hi 'the brown dog' {} +

참고 grep때까지 시작되지 않습니다는 find이에 씹어 때문에 일부 초기 지연이있을 것이다 충분한 파일을 발견했다. 그리고 find이전 grep으로 돌아올 때까지 더 많은 파일을 계속 검색하지 않습니다 . 큰 파일 목록을 할당하고 전달하면 약간의 영향을 미치므로 grep -rsymlink를 따르거나 장치 내부를 보지 않는 것보다 효율성이 떨어질 수 있습니다.

GNU 도구로 :

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

위와 grep같이 가능한 적은 인스턴스가 실행되지만 find첫 번째 grep호출이 첫 번째 배치 내부를 찾는 동안 더 많은 파일을 계속 찾습니다. 그러나 이점이 될 수도 있고 아닐 수도 있습니다. 예를 들어 회전식 하드 드라이브에 저장된 데이터 find와 grep디스크의 다른 위치에 저장된 데이터에 액세스하면 디스크 헤드가 지속적으로 이동하여 디스크 처리량이 느려집니다. 는 RAID 설정에서 (여기서 find와 grep다른 디스크에 액세스 할 수 있습니다) 또는 SSD를에, 그 긍정적 인 변화를 만들 수 있습니다.

RAID 설정에서 여러 개의 동시 grep 호출을 실행 하면 상황이 개선 될 수도 있습니다. 여전히 3 개의 디스크가있는 RAID1 스토리지의 GNU 도구

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

성능이 크게 향상 될 수 있습니다. 그러나 두 grep번째 grep명령 은 첫 번째 명령 을 채우는 데 충분한 파일이 발견 된 후에 만 ​​시작 됩니다. 더 빨리 발생 하도록 -n옵션을 추가 할 수 있으며 호출 xargs당 더 적은 파일을 전달할 수 있습니다 grep.

또한 xargs출력을 터미널 장치 이외의 것으로 리디렉션하는 경우 grepss가 출력을 버퍼링하기 시작 하므로 s의 출력 grep이 잘못 인터리브 될 수 있습니다. 그 문제를 해결하기 위해 stdbuf -oL(GNU 또는 FreeBSD와 같이 사용 가능한 경우) 그것들 을 사용해야 하거나 (여전히 긴 줄에 문제가있을 수 있습니다 (일반적으로 4KiB 이상)) 각각 별도의 파일로 출력을 작성하고 연결해야합니다 결국.

여기서 찾고있는 문자열은 고정되어 있지 않으므로 (regexp가 아님) -F옵션을 사용하면 차이가 발생할 수 있습니다 ( grep구현물이 이미 최적화하는 방법을 알고 있지는 않음 ).

큰 차이를 만들 수있는 또 다른 것은 멀티 바이트 로캘 인 경우 로캘을 C로 고정하는 것입니다.

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

내부보고 피하려면 /proc, /sys..., 사용 -xdev당신이 검색 할 파일 시스템 및 지정

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

또는 명시 적으로 제외하려는 경로를 정리하십시오.

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

나는 누군가가 나를 {@ 및 +의 의미로 설명 할 수 있다고 생각하지 않는다. 사용중인 Solaris 상자에서 exec, grep 또는 find에 대한 매뉴얼 페이지에서 볼 수있는 것은 없습니다. 쉘이 파일 이름을 연결하고 grep에 전달합니까?

3
@Poldie, 솔라리스 매뉴얼 페이지 의 -exec술어 설명에 명확하게 설명되어 있습니다
— Stéphane Chazelas

아. 넵. 맨 페이지 내에서 검색하는 동안 {문자를 이스케이프하지 않았습니다. 귀하의 링크가 더 좋습니다; 맨 페이지를 읽는 것이 끔찍하다는 것을 알았습니다.

1
3 개의 디스크가있는 RAID1? 얼마나 이상한 ...
— Tink

1
@tink, yes RAID1이 2 개 이상의 디스크에 있습니다. 2 개의 디스크에 비해 3 개의 디스크를 사용하면 중복성과 읽기 성능이 향상되는 반면 쓰기 성능은 거의 동일합니다. 2 개의 디스크가 아닌 3 개의 디스크를 사용하면 복사본 중 하나에서 비트가 약간 뒤집어 질 때 2 개의 디스크를 사용하는 동안 3 개의 복사본을 모두 확인하여 올바른 것을 알 수 있습니다. 정말 말해
— Stéphane Chazelas

13

는 IF *에서 grep호출이 당신에게 중요하지 않습니다 그 첫 번째는 하나의 인스턴스 만 아니라보다 효율적으로해야 grep시작되고 포크는 무료로하지 않습니다. 대부분의 경우 더 빠르지 *만 가장자리의 경우 정렬이 반대로 할 수 있습니다.

기타가있을 수 있습니다 find- grep많은 작은 파일을 더 나은 특히 작동 구조. 대량의 파일 항목과 inode를 한 번에 읽으면 회전하는 미디어의 성능이 향상 될 수 있습니다.

그러나 syscall 통계를 살펴 보겠습니다.

검색

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

grep 만

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total

1
전체 파일 시스템을 검색하는 규모에서 포크는 무시할 수 있습니다. I / O는 줄이고 자합니다.
— Gilles

OP의 오류이지만 비교가 올바르지 -r않으므로를 grep사용할 때 플래그를 제거해야합니다 find. open발생한 수를 비교하여 동일한 파일을 반복해서 검색 한 것을 볼 수 있습니다 .
— qwertzguy

1
@qwertzguy, 아니, 인수가 디렉토리 -r라는 -type f것을 보장하지 않기 때문에 무해해야합니다 . 여러 open()s는 grep각 호출 (라이브러리, 현지화 데이터 ...) (내 답변 btw에 대한 편집 감사)에서 열린 다른 파일에 더
— 가깝습니다.

5

SSD를 사용하고 탐색 시간을 무시할 수 있다면 GNU 병렬을 사용할 수 있습니다.

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

find찾은 내용에 따라 최대 8 개의 grep 프로세스를 동시에 실행합니다 .

이렇게하면 하드 디스크 드라이브가 작동하지만 SSD가이를 잘 처리해야합니다.


-1

이것에 대해 고려해야 할 또 하나의 사항은 다음과 같습니다.

grep 이 재귀 적으로 수행해야 할 디렉토리에 시스템의 nofile 설정 보다 많은 파일이 포함되어 있습니까? (예 : 열린 파일 핸들 수, 대부분의 Linux 배포판에서 기본값은 1024입니다)

그렇다면 특정 버전의 grep 이 최대 열린 파일 핸들 설정보다 많은 파일을 가진 디렉토리에 도달 할 때 인수 목록에 너무 긴 오류가 발생하기 때문에 find 는 확실히 진행 방법 입니다.

그냥 내 2 Just.


1
왜 grep폭탄 이 터질까요? 후행 경로를 제공 /하고 사용 하는 경우 적어도 GNU grep을 사용 -R하여 단순히 디렉토리를 반복합니다. 쉘 은 쉘 globs의를 제공하지 않는 한 아무것도 확장하지 않을된다. 따라서 주어진 예 ( /*)에서 /단순히 열거 될 하위 폴더가 아닌 물질 의 내용 만 grep쉘의 인수로 전달되지 않습니다.
— 0xC0000022L

OP가 재귀 적으로 검색을 요구하는 것을 고려할 때 (예 : "grep -r -i '갈색 개'/ *") GNU grep (최소 버전 2.9) 폭탄이 다음과 같이 나오는 것을 보았습니다 . "-bash : / bin / grep : 인수 목록이 너무 깁니다 "하위 디렉토리가 140,000 개가 넘는 디렉토리에서 사용한 OP를 정확하게 검색합니다.
— B.Kaatz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.