Wypróbowałem oba polecenia, a polecenie find | grep "filename" jest wiele razy wolniejsze niż proste find "filename".

Jakie byłoby właściwe wyjaśnienie tego zachowania?

Komentarze

  • Ty wypisują każdy plik za pomocą polecenia find, a następnie przekazują dane do przetwarzania grep. W przypadku użycia funkcji find ' jako własnego, brakuje kroku przekazywania każdego wymienionego pliku do programu grep w celu przeanalizowania wyniku. Będzie to zatem szybsze.
  • W jakim sensie wolniej? Czy wykonanie poleceń zajmuje trochę czasu?
  • Nie mogę ' odtworzyć tego lokalnie. Jeśli już, time find "$HOME" -name '.profile' zgłasza dłuższy czas niż time find "$HOME" | grep -F '.profile'. (17s vs 12s).
  • @JenniferAnderson Prowadziłem oba wielokrotnie. 17 i 12 sekund to średnie. I tak, odmiana grep będzie pasować w dowolnym miejscu wyniku find, natomiast dopasowanie z find -name będzie pasować tylko dokładnie (w tym przypadku).
  • Tak, find filename byłoby szybkie . W pewnym sensie założyłem, że to pomyłka i że OP oznaczał find -name filename. Z find filename, tylko filename zostanie zbadany (i nic więcej).

Odpowiedz

(Zakładam, że GNU find tutaj)

Używając tylko

find filename 

byłoby szybkie, ponieważ zwróciłoby po prostu filename lub nazwy wewnątrz filename, jeśli jest to katalog, lub błąd, jeśli ta nazwa nie istnieje w bieżącym katalogu. Jest to bardzo szybka operacja, podobna do ls filename (ale rekurencyjna, jeśli filename jest katalogiem).

W Natomiast

find | grep filename 

pozwoliłoby find wygenerować listę wszystkich nazwisk z bieżący katalog i poniżej, który grep będzie filtrował. To oczywiście byłaby znacznie wolniejsza operacja.

Zakładam, że to, co faktycznie zamierzone było

find . -type f -name "filename" 

To wyszukałoby filename jako nazwę zwykłego pliku w dowolnym miejscu w bieżący katalog lub niższy.

Będzie to równie szybkie (lub porównywalnie szybkie) jak find | grep filename, ale grep rozwiązanie dopasowałoby filename do pełnej ścieżki każdej znalezionej nazwy, podobnie jak -path "*filename*" zrobiłby z find.


Zamieszanie wynika z niezrozumienia sposobu, w jaki find działa.

Narzędzie pobiera kilka ścieżek i zwraca wszystkie nazwy znajdujące się pod tymi ścieżkami.

Następnie możesz ogranicz zwracane nazwy za pomocą różnych testów, które mogą działać na nazwie pliku, ścieżce, sygnaturze czasowej, rozmiarze pliku, typie pliku itp.

Kiedy mówisz

find a b c 

pytasz find o wyświetlenie wszystkich nazw dostępnych w trzech ścieżkach a, b i c. Jeśli są to nazwy zwykłych plików w bieżącym katalogu, zostaną one zwrócone. Jeśli któryś z nich jest nazwą katalogu, zostanie zwrócony wraz ze wszystkimi innymi nazwami w tym katalogu.

Kiedy to zrobię

find . -type f -name "filename" 

Generuje listę wszystkich nazw w bieżącym katalogu (.) i poniżej. Następnie ogranicza nazwy do zwykłych plików, tj. Nie do katalogów itp., Z -type f. Następnie istnieje dalsze ograniczenie do nazw, które pasują do filename, używając -name "filename". Ciąg filename może być wzorcem globalnym w nazwie pliku, takim jak *.txt (pamiętaj tylko o zacytowaniu!).

Przykład:

Wydaje się, że następujące polecenie „znajduje” plik o nazwie .profile w moim katalogu domowym:

$ pwd /home/kk $ find .profile .profile 

Ale w rzeczywistości zwraca wszystkie nazwy ze ścieżki .profile (jest tylko jedna nazwa i jest to ten plik).

Następnie cd o jeden poziom wyżej i próbuję ponownie:

$ cd .. $ pwd /home $ find .profile find: .profile: No such file or directory 

find polecenie nie może teraz znaleźć żadnej ścieżki o nazwie .profile.

Jeśli jednak popatrzę na bieżący katalog, a następnie ograniczę zwracane nazwy tylko do .profile , znajdzie stamtąd również:

$ pwd /home $ find . -name ".profile" ./kk/.profile 

Komentarze

  • find filename zwróci tylko filename, jeśli filename nie był typu katalog (lub był typu katalog, ale sam nie ma żadnego wpisu)

Odpowiedź

Nietechniczne wyjaśnienie: Poszukiwanie Jacka w tłumie jest szybszy niż szukanie wszystkich w tłumie i eliminowanie wszystkich z wyjątkiem Jacka.

Komentarze

  • Problem polega na tym, że OP oczekuje, że Jack bądź jedyną osobą w tłumie. Jeśli tak, ' mają szczęście. find jack wyświetli jack jeśli ' to plik o nazwie jack lub wszystkie nazwy w katalogu, jeśli jest to katalog ' sa. To ' to niezrozumienie, jak działa find.

Odpowiedź

Jeszcze nie zrozumiałem problemu, ale mogę podać więcej informacji.

Podobnie jak w przypadku Kusalanandy połączenie find | grep jest wyraźnie szybszy w moim systemie, co nie ma większego sensu. Na początku założyłem jakiś problem z buforowaniem; że zapis do konsoli spowalnia czas do następnego wywołania systemowego w celu odczytania nazwy następnego pliku. Zapis do potoku jest bardzo szybki: około 40 MiB / s nawet dla zapisów 32-bajtowych (na moim raczej wolnym systemie; 300 MiB / s dla bloku o rozmiarze 1 MiB). Dlatego założyłem, że find może szybciej czytać z systemu plików podczas zapisywania do potoku (lub pliku), tak że dwie operacje odczytu ścieżek plików i zapisu do konsoli mogą działać równolegle ( czego find jako proces pojedynczego wątku nie może wykonać samodzielnie.

To find „błąd”

Porównanie dwóch wywołań

:> time find "$HOME"/ -name "*.txt" >/dev/null real 0m0.965s user 0m0.532s sys 0m0.423s 

i

:> time find "$HOME"/ >/dev/null real 0m0.653s user 0m0.242s sys 0m0.405s 

pokazuje, że find robi coś niesamowicie głupiego (cokolwiek to może być). Po prostu okazuje się całkiem niekompetentny w wykonywaniu -name "*.txt".

Może zależeć od stosunku wejścia / wyjścia

Można by pomyśleć, że find -name wygrywa, jeśli jest bardzo mało do napisania. Ale to staje się po prostu bardziej krępujące dla find. Traci, nawet jeśli w ogóle nie ma nic do napisania wobec 200 tys. plików (13 mln potoków) dla grep:

time find /usr -name lwevhewoivhol 

find może być tak szybkie, jak grep, chociaż

Okazuje się, że find „głupota z name nie obejmuje innych testów. Zamiast tego użyj wyrażenia regularnego i problem zniknie:

:> time find "$HOME"/ -regex "\.txt$" >/dev/null real 0m0.679s user 0m0.264s sys 0m0.410s 

Myślę, że można to uznać za błąd. Czy ktoś chce zgłosić błąd? Moja wersja to find (GNU findutils) 4.6.0

Komentarze

  • Jak powtarzalne są twoje czasy? Jeśli najpierw wykonałeś test -name, mógł być wolniejszy z powodu braku buforowania zawartości katalogu. (Podczas testowania -name i -regex stwierdzam, że zajmują mniej więcej tyle samo czasu, przynajmniej po uwzględnieniu efektu pamięci podręcznej. oczywiście, może to być po prostu inna wersja find …)
  • @psmears Oczywiście kilka razy przeprowadzałem te testy. O problemie buforowania wspomniano nawet w komentarzach do pytania przed pierwszą odpowiedzią. Moja find wersja to find (GNU findutils) 4.6.0
  • Dlaczego to zaskakujące, że dodawanie -name '*.txt' zwalnia find? Musi wykonać dodatkową pracę, testując każdą nazwę pliku.
  • @Barmar Z jednej strony ta dodatkowa praca może być wykonana bardzo szybko. Z drugiej strony ta dodatkowa praca oszczędza inną pracę. find musi zapisać mniej danych. A zapis do potoku jest znacznie wolniejszą operacją.
  • Zapis na dysku jest bardzo wolny, zapis do potoku nie jest taki zły, po prostu kopiuje do bufora jądra. Zauważ, że w pierwszym teście pisanie więcej do /dev/null w jakiś sposób zużywa mniej czasu systemowego.

Odpowiedź

Uwaga : Zakładam, że masz na myśli find . -name filename (w przeciwnym razie „szukasz różnych rzeczy; find filename w rzeczywistości szuka ścieżki o nazwie nazwa pliku , może zawierać prawie żadnych plików, więc kończy się bardzo szybko).


Załóżmy, że masz katalog zawierający pięć tysięcy plików. W większości systemów plików te pliki są w rzeczywistości przechowywane w strukturze drzewa , co pozwala na szybkie zlokalizowanie dowolnego pliku.

Kiedy więc poprosisz find o zlokalizowanie pliku, którego nazwa wymaga tylko sprawdzenia, find zapyta dla tego pliku, i tylko tego pliku, do bazowego systemu plików, który odczyta bardzo niewiele stron z pamięci masowej. Więc jeśli system plików jest wart swojej soli, ta operacja będzie przebiegać znacznie szybciej niż przechodzenie przez całe drzewo w celu pobrania wszystkich wpisów.

Kiedy prosisz o zwykły find, ale to właśnie „właśnie to robisz, przechodzisz przez całe drzewo, czytając. Każdy. Pojedynczy. Wpis. Przy dużych katalogach, może to być problem (jest to dokładnie powód, dla którego kilka programów, które muszą przechowywać dużo plików na dysku, utworzy „drzewa katalogów” o głębokości dwóch lub trzech elementów: w ten sposób każdy liść musi przechowywać tylko mniej plików) .

Odpowiedź

Załóżmy, że plik / john / paul / george / ringo / beatles istnieje i plik, którego szukasz nazywa się „kamienie”

find / stones 

find porówna „beatles” z „kamieniami” i upuści go, gdy „s” i „b” nie pasują .

find / | grep stones 

W tym przypadku find przekaże „/ john / paul / george / ringo / beatles” do grep i grep wil Muszę przejść przez całą ścieżkę, zanim określę, czy pasuje.

Dlatego grep wykonuje znacznie więcej pracy, dlatego zajmuje to więcej czasu.

Komentarze

  • Czy już próbowałeś?
  • Koszt porównań ciągów (niezwykle prosty i tani) jest całkowicie przyćmiony przez IO (lub po prostu wywołanie systemowe, jeśli jest buforowane) przeszukiwania katalogów.
  • grep isn ' ta porównanie ciągów, porównanie wyrażeń regularnych, co oznacza, że musi przejść przez cały ciąg, dopóki nie znajdzie mecz lub dobiegnie końca. Wyszukiwania katalogów są takie same bez względu na wszystko.
  • @Paranoid Hm, o jakiej wersji find mówisz? To ' najwyraźniej nie jest czymś takim, jak find ja ' używany w Debianie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *