Na verdade, é o contrário. o comando grep tende a ser mais eficiente em geral.
Vou trabalhar em um instantâneo da árvore do Portage do Gentoo, que está disponível publicamente se você quiser experimentar.
$ time find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null
real 0m1.184s
user 0m0.033s
sys 0m0.130s
$ time grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null
real 0m0.017s
user 0m0.007s
sys 0m0.010s
Vamos ver quais funções são mais chamadas para cada uma:
$ (strace find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null) |& sed 's/[({].*//g' | sort | uniq -c | sort -r | head -n 10
3574 fcntl
1597 close
794 newfstatat
794 getdents
689 wait4
689 clone
689 --- SIGCHLD
404 fstat
397 openat
20 mmap
$ (strace grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null) |& sed 's/[({].*//g' | sort | uniq -c | sort -r | head -n 10
2779 fcntl
1493 close
1382 read
1096 fstat
1087 openat
794 getdents
792 newfstatat
691 ioctl
689 lseek
25 write
Veja também as chamadas que foram longas:
$ (strace -T find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null) |& sed 's/\(.*\)<\(.*\)>/ /g' | sort -nk1r | head -n10
exit_group(0) = ?
0.001884 wait4(29725, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29725
0.001879 wait4(29475, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29475
0.001813 wait4(29430, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29430
0.001812 wait4(30089, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30089
0.001807 wait4(29722, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29722
0.001795 wait4(29645, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29645
0.001794 wait4(29848, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29848
0.001759 wait4(30032, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30032
0.001754 wait4(30093, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30093
$ (strace -T grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null) |&
exit_group(0) = ?
0.002336 fcntl(3, F_SETFD, FD_CLOEXEC) = 0
0.000460 read(3, "7ELF $ time find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} + > /dev/null
real 0m0.027s
user 0m0.010s
sys 0m0.013s
$ time find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null
real 0m1.184s
user 0m0.033s
sys 0m0.130s
$ time grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null
real 0m0.017s
user 0m0.007s
sys 0m0.010s
$ (strace find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null) |& sed 's/[({].*//g' | sort | uniq -c | sort -r | head -n 10
3574 fcntl
1597 close
794 newfstatat
794 getdents
689 wait4
689 clone
689 --- SIGCHLD
404 fstat
397 openat
20 mmap
$ (strace grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null) |& sed 's/[({].*//g' | sort | uniq -c | sort -r | head -n 10
2779 fcntl
1493 close
1382 read
1096 fstat
1087 openat
794 getdents
792 newfstatat
691 ioctl
689 lseek
25 write
$ (strace -T find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} \; > /dev/null) |& sed 's/\(.*\)<\(.*\)>/ /g' | sort -nk1r | head -n10
exit_group(0) = ?
0.001884 wait4(29725, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29725
0.001879 wait4(29475, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29475
0.001813 wait4(29430, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29430
0.001812 wait4(30089, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30089
0.001807 wait4(29722, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29722
0.001795 wait4(29645, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29645
0.001794 wait4(29848, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 29848
0.001759 wait4(30032, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30032
0.001754 wait4(30093, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30093
$ (strace -T grep -r --include '*.ebuild' DEPEND /usr/portage/sys-apps/ > /dev/null) |&
exit_group(0) = ?
0.002336 fcntl(3, F_SETFD, FD_CLOEXEC) = 0
0.000460 read(3, "7ELF $ time find /usr/portage/sys-apps/ -name '*.ebuild' -exec grep DEPEND {} + > /dev/null
real 0m0.027s
user 0m0.010s
sys 0m0.013s
%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0'C6%pre%%pre%%pre%"..., 832) = 832
0.000313 close(3) = 0
0.000295 execve("/bin/grep", ["grep", "-r", "--include", "*.ebuild", "DEPEND", "/usr/portage/sys-apps/"], [/* 75 vars */]) = 0
0.000276 fcntl(3, F_SETFD, FD_CLOEXEC) = 0
0.000265 getdents(3, /* 244 entries */, 32768) = 7856
0.000233 fstat(3, {st_mode=S_IFREG|0644, st_size=826, ...}) = 0
0.000162 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
0.000137 lseek(3, 1402, 0x4 /* SEEK_??? */) = -1 ENXIO (No such device or address)
%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0'C6%pre%%pre%%pre%"..., 832) = 832
0.000313 close(3) = 0
0.000295 execve("/bin/grep", ["grep", "-r", "--include", "*.ebuild", "DEPEND", "/usr/portage/sys-apps/"], [/* 75 vars */]) = 0
0.000276 fcntl(3, F_SETFD, FD_CLOEXEC) = 0
0.000265 getdents(3, /* 244 entries */, 32768) = 7856
0.000233 fstat(3, {st_mode=S_IFREG|0644, st_size=826, ...}) = 0
0.000162 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
0.000137 lseek(3, 1402, 0x4 /* SEEK_??? */) = -1 ENXIO (No such device or address)
Bastante interessante, você vê nesta duração que a saída está esperando muito, enquanto o grep faz algumas coisas necessárias para iniciar e parar o processo. As chamadas de espera levam mais de 0,001s, enquanto as chamadas de busca diminuem para um nível constante de 0,0002s.
Se você olhar para as chamadas wait4 na saída da contagem, você notará que há uma quantidade igual de chamadas clonadas e sinais SIGCHLD ocorrendo; isso ocorre porque o find chama o processo do grep para cada arquivo que encontra , e é aí que sua eficiência sofre, já que a clonagem e a espera são caras.
Há ocasiões em que não sofre ; você pode obter um conjunto de arquivos pequeno o suficiente para não haver muita sobrecarga de iniciar vários processos do grep, você também pode ter um disco muito lento que negligencie a sobrecarga de iniciar um novo processo, e provavelmente há outras razões também. No entanto, ao comparar a velocidade, muitas vezes observamos o quão bem uma ou outra abordagem é dimensionada e não analisamos casos especiais de canto.
No seu caso, você mencionou que " É por isso que eu sinto que é a maneira como o" grep "visita a árvore de diretórios que é ineficiente em comparação com" find ". " caso; como você pode ver, 1382 chamadas de leitura foram feitas, enquanto o find não faz isso, isso torna o grep mais E / S intensivo para você.
TL; DR: Para ver por que os intervalos são ineficientes, tente fazer essa análise novamente e identifique o problema no seu caso, para saber por que seus dados específicos e tarefas não são eficientes no grep; você descobrirá como o grep diferente pode se comportar no seu caso de canto ...
Assim, como outros sugeriram, você deve certificar-se de que ele não chame grep para cada arquivo, o que pode ser feito substituindo \;
por +
no final.
Como você pode ver, 0.027s chega bem perto de 0.017s; a diferença é principalmente atribuída ao fato de que ele ainda tem que chamar tanto o find quanto o grep ao invés de apenas grep sozinho. Ou, como mostrado nos comentários, em alguns sistemas, o +
permite melhorar o grep.