Co jest bardziej wydajne w wyszukiwaniu plików w całym systemie plików zawierających ciąg: rekurencyjne grep czy wyszukiwanie za pomocą grep w instrukcji exec? Zakładam, że find byłoby bardziej wydajne, ponieważ możesz przynajmniej przeprowadzić filtrowanie, jeśli znasz rozszerzenie pliku lub wyrażenie regularne, które pasuje do nazwy pliku, ale kiedy znasz tylko -type f
, co jest lepsze ? GNU grep 2.6.3; find (GNU findutils) 4.4.2
Przykład:
grep -r -i "the brown dog" /
find / -type f -exec grep -i "the brown dog" {} \;
Komentarze
Odpowiedź
Nie mam pewności:
grep -r -i "the brown dog" /*
naprawdę miałeś na myśli. To oznaczałoby grep rekurencyjnie w wszystkie nieukryte pliki i katalogi w /
(ale nadal zajrzyj do ukrytych plików i katalogów w nich).
Zakładając, że chodziło Ci o:
grep -r -i "the brown dog" /
Kilka uwag:
- Nie wszystkie
grep
implementacje obsługują-r
. Wśród tych, które to robią, zachowania są różne: niektóre podążają za dowiązaniami symbolicznymi do katalogów podczas przechodzenia przez drzewo katalogów (co oznacza, że możesz skończyć szukając se w tym samym pliku lub nawet w nieskończonych pętlach), niektóre nie. Niektórzy zajrzą do plików urządzeń (i zajmie to trochę czasu na przykład w/dev/zero
) lub potokach lub plikach binarnych …, inni nie. - Jest to wydajne, ponieważ
grep
zaczyna przeglądać pliki, gdy tylko je wykryje. Ale gdy szuka w pliku, nie szuka już więcej plików do przeszukania (co jest prawdopodobnie tak samo dobrze w większości przypadków)
Twój:
find / -type f -exec grep -i "the brown dog" {} \;
(usunięto -r
co nie ma tu sensu) jest strasznie nieefektywne, ponieważ” uruchamiasz jeden grep
na plik. ;
powinno być używane tylko w przypadku poleceń akceptujących tylko jeden argument. Co więcej, ponieważ grep
wygląda tylko w jednym pliku, nie wypisze nazwy pliku, więc nie będziesz wiedział, gdzie są dopasowania.
Ty ” nie zaglądasz do plików urządzeń, potoków, dowiązań symbolicznych …, „nie śledzisz dowiązań symbolicznych, ale” nadal potencjalnie patrzysz wewnątrz rzeczy, takich jak /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
byłoby dużo lepsze, ponieważ wykonanych byłoby jak najmniej grep
poleceń. Otrzymasz nazwę pliku, chyba że ostatnie uruchomienie ma tylko jeden plik. W tym celu lepiej użyć:
find / -type f -exec grep -i "the brown dog" /dev/null {} +
lub z GNU grep
:
find / -type f -exec grep -Hi "the brown dog" {} +
Pamiętaj, że grep
nie zostanie uruchomiony przed find
znalazł wystarczająco dużo plików, aby je przeżuć, więc wystąpi początkowe opóźnienie. Ponadto find
nie będzie kontynuować wyszukiwania kolejnych plików do czasu powrotu poprzedniego grep
. Przydzielanie i przekazywanie dużej listy plików ma pewien (prawdopodobnie nieistotny) wpływ, więc w sumie prawdopodobnie będzie mniej wydajne niż grep -r
, który nie podąża za linkiem symbolicznym ani nie wygląda wewnątrz urządzeń.
Z narzędziami GNU:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Jak wyżej, tylko kilka grep
instancje, jak to możliwe, zostaną uruchomione, ale find
będzie szukał więcej plików, podczas gdy pierwsze wywołanie grep
będzie szukało wewnątrz pierwszej partii. To jednak może być zaletą, ale nie musi.Na przykład w przypadku danych przechowywanych na obrotowych dyskach twardych find
i grep
dostęp do danych przechowywanych w różnych lokalizacjach na dysku spowolni działanie dysku przepustowość, powodując ciągły ruch głowicy dysku. W konfiguracji RAID (gdzie find
i grep
mogą uzyskiwać dostęp do różnych dysków) lub na dyskach SSD, może to mieć pozytywny wpływ.
W konfiguracji RAID uruchomienie kilku współbieżnych wywołań grep
może również poprawić sytuację. Nadal z narzędziami GNU na pamięci masowej RAID1 z 3 dyskami
find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog"
może znacznie zwiększyć wydajność. Należy jednak pamiętać, że drugie grep
zostanie uruchomione dopiero po znalezieniu wystarczającej liczby plików do zapełnienia pierwszego polecenia grep
. Możesz dodać opcję -n
do xargs
, aby stało się to wcześniej (i przekazać mniej plików na grep
).
Zauważ również, że jeśli przekierowujesz xargs
wyjście na wszystko inne niż urządzenie końcowe, wówczas greps
s zaczną buforować swoje dane wyjściowe, co oznacza, że dane wyjściowe tych grep
s będą prawdopodobnie nieprawidłowo przeplecione. Musisz użyć stdbuf -oL
(jeśli są dostępne, jak na GNU lub FreeBSD) na nich, aby obejść ten problem (możesz nadal mieć problemy z bardzo długimi wierszami (zwykle> 4KiB)) lub zapisz swoje wyjście w osobnym pliku i połącz je wszystkie na końcu.
Tutaj ciąg, którego szukasz, jest ustalony (nie jest to wyrażenie regularne), więc użycie opcji -F
może mieć znaczenie (mało prawdopodobne, grep
implementacje już wiedzą, jak to zoptymalizować).
Kolejna rzecz, ld robi dużą różnicę, ustawiając ustawienie regionalne na C, jeśli „jesteś w lokalizacji wielobajtowej:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog"
Aby uniknąć zaglądania do środka /proc
, /sys
…, użyj -xdev
i określ systemy plików, w których chcesz przeszukać:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Lub usuń ścieżki, które chcesz wyraźnie wykluczyć:
LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \ -type f -exec grep -i "the brown dog" /dev/null {} +
Komentarze
- Nie ' nie przypuszczam, że ktoś może wskazać mi zasób – lub wyjaśnić – co {} i + oznaczają. Nie ' niczego nie widzę na stronach podręcznika dla exec, grep ani nie znajduję w oknie Solaris i ' m używam. Czy to tylko powłoka łączy nazwy plików i przekazuje je do grepa?
- @Poldie, to ' jest jasno wyjaśnione w opisie
-exec
predykat na stronie podręcznika systemu Solaris - Ach, tak. Nie ' nie uciekłem z mojego znaku {char podczas wyszukiwania na stronie podręcznika. Twój link jest lepszy; Strasznie się czyta strony podręcznika.
- RAID1 z 3 dyskami? Jakie to dziwne …
- @tink, tak RAID1 jest na 2 lub więcej dyskach. Dzięki 3 dyskom w porównaniu z 2 dyskami zwiększasz nadmiarowość i wydajność odczytu, podczas gdy wydajność zapisu jest mniej więcej taka sama. Mając 3 dyski w przeciwieństwie do 2, oznacza to, że możesz również poprawiać błędy, ponieważ gdy trochę przewróci się na jednej z kopii, ' będziesz w stanie stwierdzić, który jest prawidłowy, sprawdzając wszystkie 3 kopie, mając 2 dyski, możesz ' naprawdę powiedzieć.
Odpowiedź
Jeśli *
w wywołaniu grep
nie jest dla Ciebie ważne, to pierwsze powinno być bardziej wydajne, ponieważ tylko jedno Wystąpienie grep
jest uruchamiane, a widelce nie są wolne. W większości przypadków będzie to szybsze nawet z *
, ale w skrajnych przypadkach sortowanie mogłoby to odwrócić.
Mogą istnieć inne find
– grep
struktury, które działają lepiej, zwłaszcza z wieloma małymi Pliki. Odczytywanie dużych ilości wpisów plików i i-węzłów na raz może poprawić wydajność obracania multimediów.
Ale spójrzmy na statystyki wywołań systemowych:
znajdź
> 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 only
> 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
Komentarze
- W skali przeszukiwania całego systemu plików rozwidlenia są pomijalne. I / O to to, co chcesz zmniejszyć.
- Chociaż jest to błąd z OP, porównanie jest niepoprawne, powinieneś usunąć flagę
-r
zgrep
podczas korzystania zfind
. Możesz zobaczyć, że przeszukiwał w kółko te same pliki, porównując liczbęopen
, które się wydarzyły. - @qwertzguy, nie,
-r
powinno być nieszkodliwe, ponieważ-type f
gwarantuje, że żaden z argumentów nie jest katalogiem. Wieleopen()
jest bardziej prawdopodobne w przypadku innych plików otwieranych przezgrep
przy każdym wywołaniu (biblioteki, dane lokalizacyjne …) ( dziękuję za edycję mojej odpowiedzi btw)
Odpowiedź
Jeśli jesteś na dysku SSD i szukasz czasu jest pomijalna, możesz użyć równoległego GNU:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Spowoduje to wykonanie do 8 procesów grep w tym samym czasie w oparciu o find
znaleziony.
Spowoduje to zepsucie dysku twardego, ale dysk SSD powinien sobie z nim całkiem nieźle poradzić.
Odpowiedź
Jeszcze jedna rzecz do rozważenia w tej sprawie jest następująca.
Czy którykolwiek z katalogów, przez które grep będzie musiał przechodzić rekurencyjnie, będzie zawierał więcej pliki niż ustawienie nofile w systemie? (np. liczba otwartych uchwytów plików, domyślna to 1024 w większości dystrybucji Linuksa)
Jeśli tak, to find jest zdecydowanie właściwą drogą, ponieważ niektóre wersje grep wyładuje się błędem argumentów za długa , gdy trafi do katalogu zawierającego więcej plików niż maksymalna liczba otwartych plików ustawienia uchwytów.
Tylko moje 2 ¢.
Komentarze
- Dlaczego
grep
zbombardować? Przynajmniej z GNU grep, jeśli podasz ścieżkę z końcowym/
i użyjesz-R
it ' Po prostu przejdę przez katalogi. powłoka nie ' nie rozwinie niczego, chyba że podasz shell-globs. Zatem w podanym przykładzie (/*
) liczy się tylko zawartość/
, a nie podfoldery, które zostaną po prostu wyliczone przezgrep
, nie jest przekazywany jako argument z powłoki. - Cóż, biorąc pod uwagę, że OP pytał o wyszukiwanie rekurencyjne (np. ” grep -r -i ' brązowy pies ' / * „), widziałem GNU ' s grep (co najmniej wersja 2.9) bombarduje: ” -bash: / bin / grep: Lista argumentów jest zbyt długa ” przy użyciu dokładnego wyszukiwania OP zastosowanego w katalogu zawierającym ponad 140 000 podkatalogów.
-exec {} \;
. Może być konieczne dodanie-H
(lub-h
) do opcjigrep
, aby uzyskać dokładne wyniki równoważne wyjście.-r
wgrep
za drugą