재귀 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호출이 첫 번째 배치 내부를 찾는 동안 더 많은 파일을 계속 찾습니다. 그러나 이점이 될 수도 있고 아닐 수도 있습니다. 예를 들어 회전식 하드 드라이브에 저장된 데이터 findgrep디스크의 다른 위치에 저장된 데이터에 액세스하면 디스크 헤드가 지속적으로 이동하여 디스크 처리량이 느려집니다. 는 RAID 설정에서 (여기서 findgrep다른 디스크에 액세스 할 수 있습니다) 또는 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.