Dlaczego funkcja find czasami pasuje do argumentu ścieżki wiersza poleceń?

9

W systemie Linux

cd /tmp
mkdir foo; cd foo

Teraz biegnę

find . -name 'foo'

nie daje wyniku. Podczas biegania

find /tmp/foo -name 'foo'

Daje wynik, /tmp/fooktóry nie ma dla mnie sensu. Czy ktoś może wyjaśnić, dlaczego?

York
źródło
2
znajdź seach wewnątrz podanej ścieżki, tak jak jest powiedziane. Więc jeśli wpiszesz, ./że nie pasujefoo
Costas
@Costas: Rozumiem to. Nie rozumiem tylko, dlaczego zrobiło to różnicę, jeśli podałem absolutną ścieżkę find.
York,
@York W niektórych sytuacjach nie ma właściwego zachowania. Wyobraź sobie dowiązanie symboliczne o nazwie barwskazującej plik, fooktóry znajduje się poza ścieżką wyszukiwania. Czy to będzie pasować czy nie?
Hauke ​​Laging
1
.I /tmp/foonie są takie same - są to dwa różne twarde linki do tego samego katalogu; find /tmp/foo/. -name 'foo'też niczego nie znajduje.
jimmij
@jimmij: kiedy uruchomiłem find /tmp/foo -name 'foo', prosiłem bash o znalezienie w katalogu /tmp/foopliku o nazwie „foo”. Ponieważ katalog /tmp/foojest pusty, nie powinien nic zwracać. Nie rozumiem, dlaczego powraca /tmp/foo. Z drugiej strony, kiedy uruchomiłem find . -name 'foo', prosiłem bash o to samo, tj. Znalezienie pliku w bieżącym katalogu (który tak się kiedyś stało /tmp/foo), którego nazwa to „foo”, i nie zwraca niczego, co ma sens.
York,

Odpowiedzi:

15

findprzegląda określone drzewa katalogów i ocenia podane wyrażenie dla każdego znalezionego pliku. Podróż rozpoczyna się na podanej ścieżce. Oto podsumowanie tego, jak find . -name foodziała:

  • Pierwsza ścieżka w wierszu poleceń: .
    • Czy nazwa podstawowa ( .) pasuje do wzorca foo? Nie, więc nic nie rób.
      Tak się składa, że /tmp/foojest to inna nazwa tego samego katalogu. Ale findnie wie o tym (i nie powinien próbować się tego dowiedzieć).
    • Czy ścieżka jest katalogiem? Tak, więc przejdź to. Wymień wpisy .i dla każdego wpisu wykonaj proces przejścia.
      • Katalog jest pusty: nie zawiera żadnych pozycji innych niż .i .., które findnie przechodzą rekurencyjnie. Więc praca jest skończona.

I find /tmp/foo:

  • Pierwsza ścieżka w wierszu poleceń: /tmp/foo
    • Czy nazwa podstawowa ( foo) pasuje do wzorca foo? Tak, więc warunek jest zgodny.
      • Brak warunku związanego z tym warunkiem, dlatego wykonaj domyślną akcję, która polega na wydrukowaniu ścieżki.
    • Czy ścieżka jest katalogiem? Tak, więc przejdź to. Wymień wpisy /tmp/fooi dla każdego wpisu wykonaj proces przejścia.
      • Katalog jest pusty: nie zawiera żadnych pozycji innych niż .i .., które findnie przechodzą rekurencyjnie. Więc praca jest skończona.

Zdarza się, że .i /tmp/foosą tym samym katalogu, ale to nie wystarczy, aby zagwarantować, że findma ten sam problem na obu. findKomenda ma sposobów na odróżnienie ścieżek do tego samego pliku; -nameorzecznikiem jest jednym z nich. find /tmp/foo -name foodopasowuje katalog początkowy, a także dowolny plik pod nim, który jest wywoływany foo. find . -name .pasuje tylko do katalogu początkowego ( .nigdy nie można go znaleźć podczas przechodzenia rekurencyjnego).

Gilles „SO- przestań być zły”
źródło
„nie zawiera żadnego wpisu oprócz… i…, które nie przechodzą rekurencyjnie”. Zakładam, że „nie przechodzi rekurencyjnie” odnosi się do „..”. Jeśli jest to poprawne, co znaczy „rekurencyjnie poprzecznie” w kontekście „..”?
Faheem Mitha
@ FaheemMitha „nie przechodzi rekurencyjnie” dotyczy zarówno .i ... (Jeśli findruch .rekurencyjnie, to skończyć się poprzez katalogu znowu i znowu i znowu i ...) .i ..to nie tylko specjalne oznaczenia, pojawiają się jako wpisy do katalogów.
Gilles „SO- przestań być zły”
5

Nie ma normalizacji argumentów wiersza poleceń przed zastosowaniem testów. Zatem wyniki różnią się w zależności od użytej ścieżki (jeśli zaangażowane są dowiązania symboliczne):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

W „twoim przypadku” oba połączenia dawałyby ten sam wynik, co może (bardziej) być mylące. Możesz użyć, -mindepth 1jeśli chcesz ignorować punkty początkowe (może to być inny niż POSIX).

Hauke ​​Laging
źródło
-mindepth 1Pracuje. Jednak nadal nie rozumiem, dlaczego find . -name 'foo'i find /tmp/foo -name 'foo'zachowuję się inaczej.
York,
2

(gnu) find pokazuje wszystkie dopasowania znalezione w ścieżce podanej dla polecenia, ponieważ zaczyna ono porównywanie z argumentami wiersza poleceń, stamtąd zstępuje głębiej w strukturę katalogów (w ten sposób -maxdepth 0ogranicza testy tylko do poziomu podstawowego lub argumentów wiersza poleceń, podczas gdy -mindepth 1pomija argumenty wiersza poleceń, jak man findwyjaśniono). To jest powód, dla którego find /tmp/foo -name 'foo'da jedno dopasowanie, nawet jeśli sam katalog jest pusty.

find . -name 'foo'z drugiej strony nie przyniesie żadnego rezultatu, ponieważ .(kropka) jest specjalnym plikiem, który działa jak hardlink do tego samego i-węzła, co /tmp/foo- jest jak osobna (choć specjalna) nazwa pliku, a nie dowiązanie symboliczne lub wyrażenie, które podlega rozwijanie nazw ścieżek przez powłokę. Dlatego pierwszy test zastosowany przez find w argumentach wiersza poleceń w podanym przykładzie nie pokaże żadnych dopasowań, ponieważ .faktycznie nie pasuje do wzorca nazwy zdefiniowanego w -name 'foo'. Nie robi to również, /tmp/foo/.ponieważ test -namewzorca jest wykonywany tylko na nazwie basename ścieżki (patrz man find), co tutaj znowu jest ..

Chociaż tego zachowania nie można oczekiwać ani wydawać się intuicyjne z punktu widzenia użytkownika (i tak, na początku też mnie zdezorientowałem), nie stanowi ono błędu, ale odpowiada logice i funkcjonalności opisanej na stronach man i stronach informacyjnych dla ( gnu) znajdź.

Shevek
źródło
0

Próbowałem skomentować odpowiedź Gillesa, ale było to zbyt długo, aby zmieścić się w komentarzu. Z tego powodu stawiam to jako odpowiedź na moje własne pytanie.

Wyjaśnienie Gillesa (a także Sheveka) jest jasne i ma sens. Kluczową kwestią jest to, że nie tylko findpróbuje dopasować nazwy plików dla danych ścieżek (rekurencyjnie), ale także próbuje dopasować nazwę podstawową samych ścieżek.

Z drugiej strony, czy jest jakiś dowód na to, że w ten sposób findma działać, a nie błąd? Moim zdaniem byłoby lepiej, gdyby nie powodowało to niespójności wyników, find .a find ABSOLUTE-PATHponieważ niekonsekwencja jest zawsze myląca i mogłaby zmarnować programistom wiele czasu na próbę ustalenia „co było nie tak”. W moim przypadku pisałem skrypt, a ścieżka została zaczerpnięta ze zmiennej. Aby mój skrypt działał poprawnie, myślę o tym, aby napisać find $path/. -name 'pattern'.

Wreszcie, myślę, że uzyskanie spójnych wyników findmożna osiągnąć zawsze zastępując .katalog bieżący przed kontynuowaniem.

York
źródło
-1

W fookatalogu, w którym rozpocząłeś wyszukiwanie względne, nie ma żadnego obiektu .

Masz rację, zakładając, że gfindjest on błędny, gdy zgłasza się, /tmp/foogdy używasz nazwy bezwzględnej jako katalogu startowego.

Gfindma różne odchylenia od normy, wygląda na to, że znalazłeś inny. Jeśli podoba Ci się bardziej standardowe rozwiązanie zgodne z normą, polecam, sfindże jest to część schilytools.

-namedotyczy wyników wyszukiwania w katalogu. Żaden z dwóch wspomnianych przypadków nie zwróci pozycji katalogu fooz readdir()operacji.

schily
źródło
Podejrzewam, że to też błąd. Oto wynik find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Licencja GPLv3 +: GNU GPL wersja 3 lub nowsza < gnu.org/licenses/gpl.html >
York
Cóż, sprawdziłem twoje przykładowe polecenia za pomocą find(certyfikat POSIX) gfindi sfind; problem istnieje tylko podczas używania gfind. W systemie Linux gfindnie jest instalowany pod własną nazwą, ale pod nazwą find.
schily,
3
Poprawne jest na find /tmp/foo -name foowyjście /tmp/foo. (Cc @York) Jest to zachowanie OpenBSD find, GNU find, BusyBox find, Solaris findi wszelkich innych zgodnych z POSIX find(„Podstawowa powinna ocenić jako prawda, jeśli nazwa basenowa badanej nazwy pliku pasuje do wzorca (…)” - gdy nazwa pliku to taki, który jest przekazywany jako operand ścieżki, nie readdirjest wymagane żadne połączenie).
Gilles „SO- przestań być zły”