Użyj polecenia znajdź, ale wyklucz pliki w dwóch katalogach

86

Chcę znaleźć pliki, które kończą się na _peaks.bed, ale wykluczyć pliki w folderach tmpi scripts.

Moje polecenie jest takie:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Ale to nie zadziałało. Pliki w tmpi scriptnadal będą wyświetlane folderu.

Czy ktoś ma jakieś pomysły na ten temat?

Hanfei Sun
źródło

Odpowiedzi:

190

Oto jak możesz to określić za pomocą find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Wyjaśnienie:

  • find . - Rozpocznij wyszukiwanie z bieżącego katalogu roboczego (domyślnie rekurencyjnie)
  • -type f- Określ, findże chcesz tylko pliki w wynikach
  • -name "*_peaks.bed" - Poszukaj plików z nazwą kończącą się na _peaks.bed
  • ! -path "./tmp/*" - Wyklucz wszystkie wyniki, których ścieżka zaczyna się od ./tmp/
  • ! -path "./scripts/*" - Wyklucz również wszystkie wyniki, których ścieżka zaczyna się od ./scripts/

Testowanie rozwiązania:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Byliście całkiem blisko, -nameopcja uwzględnia tylko podstawową nazwę, gdzie jako -pathrozważa całą ścieżkę =)

sampson-chen
źródło
Dobra robota. Jednak zapomniałeś o jednej z rzeczy, których chciał OP, aby znaleźć pliki kończące się na _peaks.bed.
Alex
2
Używa to wielu rozszerzeń w GNU find, ale ponieważ pytanie jest oznaczone jako Linux, nie stanowi to problemu. Dobra odpowiedź.
Jonathan Leffler,
1
Krótka uwaga: jeśli używasz .podczas początkowego monitu o wyszukiwanie, musisz użyć go w każdej wykluczonej ścieżce. Dopasowanie ścieżki jest dość ścisłe, nie wykonuje wyszukiwania rozmytego. Więc jeśli używasz, find / -type f -name *.bed" ! -path "./tmp/"to nie zadziała. musisz ! -path "/tmp"to uszczęśliwić.
peelman
3
Należy pamiętać, że znak * jest ważny. $ ! -path "./directory/*"
Thomas Bennett
3
Według stron podręcznika man: "Aby zignorować całe drzewo katalogów, używaj -prunezamiast sprawdzania każdego pliku w drzewie." Jeśli wykluczone katalogi działają bardzo głęboko lub mają mnóstwo plików i zależy Ci na wydajności, użyj -prunezamiast tego opcji.
thdoan
8

Oto jeden sposób, w jaki możesz to zrobić ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"
Alex
źródło
2
Ma to zaletę pracy z dowolną wersją find, a nie tylko z GNU find. Jednak pytanie jest oznaczone jako Linux, więc nie jest krytyczne.
Jonathan Leffler,
2

Posługiwać się

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

lub

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

lub

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

Kolejność jest ważna. Ocenia od lewej do prawej. Zawsze zaczynaj od wykluczenia ścieżki.

Wyjaśnienie

Nie używaj -not(lub !) do wykluczania całego katalogu. Użyj -prune. Jak wyjaśniono w instrukcji:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

oraz w podręczniku GNU znajdź:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

Rzeczywiście, jeśli użyjesz -not -path "./pathname", find oceni wyrażenie dla każdego węzła pod "./pathname".

wyrażenia find to tylko ocena warunków.

  • \( \)- obsługa grup (możesz użyć -path "./tmp" -prune -o -path "./scripts" -prune -o, ale jest bardziej rozwlekła).
  • -path "./script" -prune- jeśli -pathzwraca prawdę i jest katalogiem, zwraca prawdę dla tego katalogu, a nie schodzi do niego.
  • -path "./script" ! -prune- ocenia jako (-path "./script") AND (! -prune). Przywraca „zawsze prawdę” śliwki na zawsze fałszywą. Unika drukowania "./script"jako dopasowania.
  • -path "./script" -prune -false- ponieważ -prunezawsze zwraca true, możesz wykonać to -falsesamo, co !.
  • -o- operator OR. Jeśli żaden operator nie jest określony między dwoma wyrażeniami, domyślnie używany jest operator AND.

W związku z tym \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printjest rozszerzany do:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

Nadruk jest tutaj ważny, ponieważ bez niego jest rozszerzany do:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printjest dodawany przez find - dlatego przez większość czasu nie trzeba go dodawać w swoim wyrażeniu. I od tego czasu-prune zwraca prawdę, wypisze "./script" i "./tmp".

W innych nie jest to konieczne, ponieważ się zmieniliśmy -prune aby zawsze zwracać fałsz.

Wskazówka: możesz użyć, find -D opt expr 2>&1 1>/dev/nullaby zobaczyć, jak jest zoptymalizowany i rozwinięty,
find -D search expr 2>&1 1>/dev/nullaby zobaczyć, która ścieżka jest zaznaczona.

f380cedric
źródło
0

Spróbuj czegoś takiego

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

i nie zdziw się, jeśli trochę się pomyliłem. Jeśli celem jest exec (zamiast print), po prostu zastąp go w miejscu.

DrC
źródło
0

dla mnie to rozwiązanie nie działało na poleceniu exec z find, naprawdę nie wiem dlaczego, więc moje rozwiązanie jest

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Wyjaśnienie: samo co sampson-chen z dodatkami

-prune - zignoruj ​​ścieżkę proceduralną ...

-o - Następnie, jeśli brak dopasowania, wydrukuj wyniki (wyczyść katalogi i wydrukuj pozostałe wyniki)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz
al3x2ndru
źródło
Zaakceptowana odpowiedź nie zadziałała, ale to działa. Korzystanie prune, find . -path ./scripts -prune -name '*_peaks.bed' -type f. Nie wiem, jak wykluczyć wiele katalogów. Wyświetla również wykluczony katalog najwyższego poziomu, mimo że typezostał określony. Wykluczanie przez Grepa wydaje się prostsze, chyba że chcesz użyć śliwki, aby przyspieszyć operację wyszukiwania.
Mohnish,
Miałem też problem z wykluczeniem wielu katalogów, ale powyższe komentarze dały mi odpowiedź, która zadziałała. Używam wielu wystąpień „-not -path” iw każdym wyrażeniu ścieżki dołączam pełny przedrostek użyty w pierwszym parametrze, aby „znaleźć” i zakończyć każdy gwiazdką (i pominąć wszelkie kropki).
jetset
0

Możesz spróbować poniżej:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'
Jacky Jiang
źródło
2
W przypadku takiego starego pytania (4 lata!) Chcesz wyjaśnić, dlaczego ta nowa odpowiedź jest lepsza lub inna, a nie tylko „zrzucić” kod.
Nic3500