Jak zignorować niektóre nazwy plików za pomocą „znajdź”?

143

Jednym z moich ulubionych poleceń BASH jest:

find . -name '*.*' -exec grep 'SearchString' {} /dev/null \;

który przeszukuje zawartość wszystkich plików znajdujących się w bieżącym katalogu i poniżej pod kątem określonego SearchString. Jako programista czasami się przydał.

Ze względu na mój obecny projekt i strukturę bazy kodu chciałbym jednak uczynić to polecenie BASH jeszcze bardziej zaawansowanym, nie wyszukując żadnych plików, które znajdują się w lub poniżej katalogu zawierającego „.svn”, ani żadnych plików, które zakończ na „.html”

Jednak strona MAN dla wyszukiwania trochę mnie zdezorientowała. Próbowałem użyć -prune, co dało mi dziwne zachowanie. Próbując pominąć tylko strony .html (na początek), próbowałem:

find . -wholename './*.html' -prune -exec grep 'SearchString' {} /dev/null \;

i nie uzyskałem zachowania, na które liczyłem. Wydaje mi się, że brakuje mi punktu -pune. Czy moglibyście mi pomóc?

Dzięki

Cody S.
źródło
1
Tylko fyi: findnie jest wbudowanym poleceniem bash, ale osobnym programem
WakiMiko
1
Możesz przeszukać plik za pomocągrep -rl 'SearchString'
emanuele,
@ emanuele Cześć, witamy w SuperUser (i sieci Stack Exchange). To pytanie zadałem i na które udzielono odpowiedzi 2 i pół roku temu. Zazwyczaj, jeśli chcesz dodać odpowiedź na pytanie, zrób to, przewijając w dół i odpowiadając tam, zamiast w komentarzu. Ponieważ to pytanie ma już zaakceptowaną odpowiedź (tę z zielonym znacznikiem wyboru), jest mało prawdopodobne, aby twoja odpowiedź przyciągnęła wiele uwagi. FYI.
Cody S
1
Cześć, to nie jest odpowiedź na twoje pytanie. Jest to tylko wskazówka, jak stwierdzono w preambule, która służy finddo wyszukiwania w pliku.
emanuele
2
FWIW, -name '*.*'nie znajduje wszystkich plików: tylko te z .imieniem (użycie *.*zwykle jest DOS-ism, podczas gdy w Uniksie zwykle używasz tylko *do tego). Aby naprawdę dopasować je wszystkie, po prostu usunąć całkowicie argument: find . -exec .... Lub jeśli chcesz zastosować grep tylko do plików (i pominąć katalogi), to zrób find . -type f -exec ....
Stefan

Odpowiedzi:

197

Możesz użyć funkcji negowania (!) Znajdowania, aby nie dopasowywać plików o określonych nazwach:

find . ! -name '*.html' ! -path '*.svn*' -exec grep 'SearchString' {} /dev/null \;

Więc jeśli nazwa kończy się na .html lub zawiera .svn w dowolnym miejscu na ścieżce, nie będzie pasować, a więc exec nie zostanie wykonany.

Paweł
źródło
1
Powinienem jeszcze podać -name ” . gdzieś tam? Czy zrobiłbym to przed czy po negacjach?
Cody S
Czy Twoim *.*celem było dopasowanie tylko plików zawierających .? Find dopasuje wszystkie pliki w przypadku braku namedyrektywy, więc powyższe dopasuje wszystko oprócz html i svn
Paul
5
Myślę, że -wholename '*.svn*'raczej chcesz -name.
fuenfundachtzig
2
Tak, tak, aby .svnkatalogi zostały wykluczone z wyników wyszukiwania.
fuenfundachtzig
1
@Noumenon ! -name '.'powinien wykluczyć .z wyników wyszukiwania.
Paul
11

Od dawna mam ten sam problem i istnieje kilka rozwiązań, które można zastosować w różnych sytuacjach:

  • ack-grepjest rodzajem „programisty grep”, który domyślnie pomija katalogi kontroli wersji i pliki tymczasowe. manStrona wyjaśnia jak szukać tylko określonych typów plików i jak definiować własne .
  • grepWłasne --excludei --exclude-diropcje mogą być bardzo łatwo użyte do pominięcia globów plików i pojedynczych katalogów (niestety globowania dla katalogów).
  • find . \( -type d -name '.svn' -o -type f -name '*.html' \) -prune -o -print0 | xargs -0 grep ... powinien działać, ale powyższe opcje są prawdopodobnie mniej kłopotliwe na dłuższą metę.
l0b0
źródło
9

Poniższe findpolecenie wykonuje katalogi przycinania, których nazwy zawierają. .svn Chociaż nie schodzi ono do katalogu, nazwa przycinanej ścieżki jest drukowana ... ( -name '*.svn'jest przyczyną!) ..

Możesz odfiltrować nazwy katalogów poprzez grep -d skip:, które cicho pomijają takie wejściowe „nazwy katalogów”.

Z GNU grep możesz używać -Hzamiast /dev/null. Jako drobny problem uboczny: \+może być znacznie szybszy niż \;np. na 1 milion plików jednowierszowych , użycie \;zajęło 4m20s , użycie \+zajęło tylko 1,2s .

Następująca metoda używa xargszamiast -execi zakłada, że ​​nie ma żadnych znaków nowej linii \nw żadnej z nazw plików . Stosowane tutaj xargsjest bardzo podobne do znaleziska \+.

xargsmoże przekazywać nazwy plików, które zawierają kolejne spacje, zmieniając ogranicznik wejściowy na '\n'z -dopcją.

Wyklucza to katalogi, których nazwy zawierają .svn i grepują tylko te pliki, które się nie kończą .html.

find . \( -name '*.svn*' -prune  -o ! -name '*.html' \) |
   xargs -d '\n' grep -Hd skip 'SearchString'
Peter.O
źródło
1
Dziękujemy za wskazanie \+wariantu działania -exec. Brawo dla drobnych problemów ubocznych!
Christian Long
Oczywiście, ponieważ +nie jest to specjalny znak dla powłoki, nie musisz pisać \przed nią.
Scott