Jak wykluczyć katalog w find. Komenda

1378

Próbuję uruchomić findpolecenie dla wszystkich plików JavaScript, ale jak wykluczyć określony katalog?

Oto findkod, którego używamy.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
helion3
źródło
10
Jaki katalog musisz wykluczyć?
Archetypal Paul
11
Lepiej jest użyć find ... | while read -r file .... Ponadto lepiej jest akceptować i głosować odpowiedzi.
Wstrzymano do odwołania.
podczas gdy czytanie jest powolne, ponieważ
wejście
18
@mpapis podczas odczytu poprawnie obsługuje pełne linie ze spacjami.
Jean-Philippe Pellet
1
Wystarczy uruchomić ten w folderze z plikami ze spacjami w ich nazwach: for file in $(find .); do echo "$file"; done. Nazwy ze spacjami są podzielone, czego nie chcemy.
Jean-Philippe Pellet,

Odpowiedzi:

1140

Użyj -pruneprzełącznika. Na przykład, jeśli chcesz wykluczyć misckatalog, po prostu dodaj -path ./misc -prune -odo polecenia find:

find . -path ./misc -prune -o -name '*.txt' -print

Oto przykład z wieloma katalogami:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Tutaj wykluczamy dir1 , dir2 i dir3 , ponieważ w findwyrażeniach jest to działanie, które działa na podstawie kryteriów -path dir1 -o -path dir2 -o -path dir3(jeśli dir1 lub dir2 lub dir3 ), AND z type -d.

Dalsze działanie to -o printpo prostu wydrukuj.

f10bit
źródło
89
Hmm To też nie działa dla mnie, ponieważ będzie zawierać zignorowany katalog „./misc” w danych wyjściowych.
Theuni,
84
@Theuni Prawdopodobnie nie działało to dla ciebie, ponieważ nie dodałeś -print(ani żadnej innej akcji) wyraźnie później -name. W takim przypadku obie „strony” drukują na -okońcu, natomiast jeśli używasz -print, drukowana jest tylko ta strona.
Daniel C. Sobral
4
Z manpage: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Jak mogę usunąć z find, jeśli chcę wykluczyć określone katalogi z usunięcia?
Jānis Elmeris,
15
Aby usunąć cały katalog się od wykorzystania wyników: find . -not -path "./.git*". Użycie ./dir*zamiast ./dir/*usuwa katalog oraz zawartość z danych wyjściowych.
micahblu
64
To pytanie i zamieszanie w odpowiedziach pokazują, jak źle interfejs użytkownika wyszukiwania odpowiada potrzebom ludzi.
Johannes Overmann,
1931

Jeśli -pruneto nie zadziała, spowoduje to:

find -name "*.js" -not -path "./directory/*"

Uwaga: wymaga przejścia przez wszystkie niechciane katalogi.

Uwolnić się
źródło
86
Jeden z komentarzy w zaakceptowanej odpowiedzi wskazuje na problem. -prunenie wyklucza samego katalogu, wyklucza jego zawartość, co oznacza, że ​​dostaniesz niechcianą linię na wyjściu z wykluczonym katalogiem.
GetFree
95
Świetna odpowiedź. Dodam do tego, że można wykluczyć katalog na DOWOLNYM poziomie, zmieniając pierwszy .na *. więc find -name "*.js" -not -path "*/omitme/*"pomijałby pliki z katalogu o nazwie „omitme” na dowolnym poziomie głębokości.
DeeDee
83
To wciąż przemierza cały niechcianego katalogu, choć. Dodaję własną odpowiedź. :-)
Daniel C. Sobral
18
Pamiętaj jednak, że opcja przycinania nie działa tylko wtedy, gdy nie używasz jej -printjawnie.
Daniel C. Sobral
39
Lepiej byłoby powiedzieć „Jest to alternatywa dla użycia -prune”. Odpowiedzi sugerujące, że śliwka nie są błędne, po prostu nie są takie, jak byś to zrobił.
Jimbo,
458

Uważam, że łatwiej jest uzasadnić niż inne proponowane rozwiązania:

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

Ważna uwaga: ścieżki, które wpisujesz, -pathmuszą dokładnie pasować do tego find, co zostanie wydrukowane bez wykluczenia. Jeśli to zdanie myli po prostu upewnij się, aby korzystać z pełnej ścieżki przechodzące przez całą polecenia jak poniżej: . Zobacz notatkę [1], jeśli chcesz lepiej zrozumieć.find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

Wewnątrz \(i \)jest wyrażeniem, które będą pasować dokładnie build/external (patrz Ważna uwaga powyżej), i będzie, w przypadku sukcesu, unikać postępowy cokolwiek poniżej . Jest to następnie grupowane jako pojedyncze wyrażenie z nawiasem ucieczkowym i poprzedzone nim, -notco spowoduje findpominięcie wszystkiego, co pasowało do tego wyrażenia.

Można zapytać, czy dodanie -notnie spowoduje, że wszystkie inne pliki zostaną ukryte przez -pruneponowne pojawienie się, a odpowiedź brzmi „nie”. Sposób, w jaki -prunedziała to, że coś, że po jego osiągnięciu, pliki poniżej tego katalogu są stale ignorowane.

Wynika to z rzeczywistego przypadku użycia, w którym musiałem wywołać kompresor Yui w przypadku niektórych plików generowanych przez wintersmith, ale pomijać inne pliki, które należy przesłać w obecnej postaci.


Uwaga [1] : Jeśli chcesz wykluczyć /tmp/foo/bari uruchomisz wyszukiwanie w ten sposób find /tmp \(..., musisz to określić -path /tmp/foo/bar. Jeśli z drugiej strony tak się cd /tmp; find . \(...dzieje, musisz to określić -path ./foo/bar.

Daniel C. Sobral
źródło
37
Znakomita odpowiedź, dziękuję. To działa i jest skalowalne (czytelne) dla wielu wykluczeń. Jesteś panem i panem uczonym. Dziękuję za przykład wielu wykluczeń
Freedom_Ben
7
To nie działa, jeśli chcę użyć przełącznika -delete:find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
Jānis Elmeris
17
@Janis Możesz użyć -exec rm -rf {} \;zamiast -delete.
Daniel C. Sobral,
11
Analizując wyniki find, jest to naprawdę oczywiste, ale mnie to zaskoczyło. Jeśli szukasz w bieżącym katalogu (podając .jako ścieżkę wyszukiwania lub nie podając go wcale), najprawdopodobniej chcesz, aby wzorzec -pathzaczął od ./, np find -not \( -path ./.git -prune \) -type f. :
Zantier
7
Bardziej precyzyjna (i kompatybilna z POSIX) odmiana tej metody: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)po której następują wszelkie warunki, które powinny pasować do tego, czego szukasz.
Walf
217

Widać tutaj pewne zamieszanie co do preferowanej składni do pomijania katalogu.

Opinia GNU

To ignore a directory and the files under it, use -prune

Ze strony podręcznika użytkownika GNU znajdź

Rozumowanie

-pruneprzestaje findschodzić do katalogu. Samo określenie -not -pathnadal będzie przechodzić do pominiętego katalogu, ale -not -pathbędzie fałszywe przy każdym findtestowaniu każdego pliku.

Problemy z -prune

-prune robi to, do czego jest przeznaczony, ale nadal są pewne rzeczy, o które musisz zadbać, używając go.

  1. find drukuje przycięty katalog.

    • PRAWDA To zamierzone zachowanie, po prostu się do niego nie schodzi. Aby całkowicie nie wydrukować katalogu, użyj składni, która logicznie go pomija.
  2. -prunedziała tylko -printbez żadnych innych działań.

    • NIE PRAWDA . -prunewspółpracuje z dowolną akcją oprócz -delete. Dlaczego to nie działa z usuwaniem? Aby -deletedziałać, find musi przechodzić przez katalog w kolejności DFS, ponieważ -deletenajpierw usunie liście, następnie rodziców liści itp. Ale aby określić, -pruneże ma to sens, findmusi trafić do katalogu i przestać go opuszczać, co wyraźnie nie ma sensu z włączaniem -depthlub -deletewyłączaniem.

Wydajność

Przygotowałem prosty test z trzema najwyżej ocenionymi odpowiedziami na to pytanie (zastąpione -printprzez, -exec bash -c 'echo $0' {} \;aby pokazać inny przykład działania). Wyniki są poniżej

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Wniosek

Zarówno składnia f10bit, jak i składnia Daniela C. Sobrala trwały średnio 10–25 ms. Składnia GetFree , która nie korzysta -prune, zajęła 865 ms. Tak, jest to raczej skrajny przykład, ale jeśli zależy ci na czasie działania i robisz coś zdalnie intensywnego, powinieneś użyć -prune.

Uwaga : składnia Daniela C. Sobrala była lepsza z dwóch -prune; ale mocno podejrzewam, że jest to wynikiem pewnego buforowania, ponieważ zmiana kolejności, w której oba działały, dawała przeciwny wynik, podczas gdy wersja bez przycinania była zawsze najwolniejsza.

Skrypt testowy

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup
Przywróć Monikę, proszę
źródło
18
Dziękuję za bardzo dobrą analizę. Jeśli chodzi o „mocno podejrzewam, że jest to wynikiem pewnego buforowania”, możesz uruchomić następującą komendę: sudo sh -c „free && sync && echo 3> / proc / sys / vm / drop_caches && free”, aby wyczyścić pamięć podręczną (patrz unix. stackexchange.com/questions/87908/… ).
ndemou
Po kilku testach na tych dwóch ze -prunestwierdzam, że rzadko jest jakakolwiek różnica. Czy należy pamiętać, że polecenie rozpoczęcia który pierwszy skorzystają z wydajności procesora, później cpu rozgrzać> spadek wydajności przyczyna moll spowolnienie (zrobiłem oczyszczania pamięci podręcznej przed każdym poleceniu jako @ndemou sugestią)
Huy.PhamNhu
Spróbuj zmienić numer name1() name2() name3()w powyższym skrypcie testowym @BroSlow, aby zmienić kolejność wykonywania, aby uzyskać obraz tego, co powiedziałem. W prawdziwym życiu między nimi jest jednak niezauważalne.
Huy.PhamNhu
Oklaski. Dziękuję za tę wysokiej jakości odpowiedź.
Stphane
Nie powinieneś być -o co oznacza lub. więc przycinasz w pierwszym kroku, a potem zapominasz o tym w następnym.
mmm
94

To jedyny, który dla mnie działał.

find / -name MyFile ! -path '*/Directory/*'

Wyszukiwanie „MyFile” z wyłączeniem „Directory”. Podkreśl gwiazdy *.

DimiDak
źródło
13
Ta metoda działa w systemie macOS, a zaakceptowana odpowiedź nie. Wiem, że oryginalne pytanie dotyczy systemu Linux.
Xavier Rubio Jansana
5
Pamiętaj, że możesz dodać wiele ! -path '*/Directory/*'do polecenia kolejno, aby zignorować wiele katalogów
Aclwitt
Działa na MacOS, ale nie działa na Linuksie ... potwierdzono
Marcello de Sales
W docker containerjedynym współpracuje zsh -c "find..."
Marcello de Sales
@Marcello de Sales Oczywiście działa na Linuksie.
DimiDak
59

Jedną z opcji byłoby wykluczenie wszystkich wyników zawierających nazwę katalogu z grep. Na przykład:

find . -name '*.js' | grep -v excludeddir
Jozuego
źródło
44
Spowoduje to, że twoje wyszukiwanie będzie bardzo wolne
Dorian
6
Ten działał dla mnie, inne (które wykorzystują -prune) - nie.
Andron
7
Powolny w dużych wynikach, ale przydatny w mniejszych zestawach. Ale jak wykluczyć wiele katalogów za pomocą grep? Oczywiście w ten sposób: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3ale może istnieć jeden sposób grep.
Timo Kähkönen
6
Jeśli chcesz wykonywać wiele greps wtedy można byłoby lepiej pisanie go jako wyrażenia regularne: egrep -v '(dir1|dir2|dir3)'. Jednak w tym konkretnym studium przypadku lepiej byłoby wykluczyć katalogi wewnątrz findsiebie.
Laurence
1
tak, nie potrzebujesz nawiasów i lepiej użyć ^, aby upewnić się, że pasuje do nazwy katalogu na początku ciągu, np .: find. -nazwa „* .js” | egrep -v "^ \ ./ wykluczyć katalog1 | ^ \ ./ wykluczyć katalog2"
Sofija
41

Wolę -notnotację ... jest bardziej czytelna:

find . -name '*.js' -and -not -path directory
mpapis
źródło
5
Przepraszamy, to nie działa. Strona findpodręcznika man mówi: „Aby zignorować katalog i znajdujące się pod nim pliki, użyj -prune”.
Christian Davén,
8
To jest źle. Nie zapobiega to wejściu katalogu do katalogu i przejrzeniu wszystkich znajdujących się w nim plików.
GetFree
find . -iname '*' -and -not -path './somePath'nie uniemożliwia temu wejściu do wspomnianego katalogu.
Lemmings19
Pomogło mi to z .git path find . -iname '*' -not -path './.git/*'
Mark Shust w M.academy
7
@rane: Dokładniej find . -not -path "*/.git*"byłoby to, czego chcesz.
Ben,
20

Użyj opcji -prune. Coś w stylu:

find . -type d -name proc -prune -o -name '*.js'

Opcja „-type d -name proc -prune” szuka tylko katalogów o nazwie proc, które mają zostać wykluczone.
„-O” jest operatorem „LUB”.

Drew Frezell
źródło
1
To jedyne rozwiązanie typu „znajdź”, które działało dla mnie. Katalogi, które chciałem wykluczyć, NIE znajdują się bezpośrednio poniżej bieżącego katalogu roboczego.
Lambart
5
Jednak dodanie -printdo końca może poprawić wyniki. find . -type d -name .hg -prune -o -name datazignorowano zawartość (wielu) .hgkatalogów, ale wymieniono .hgsame katalogi. W -printliście wymieniono tylko te katalogi „danych”, których szukałem.
Lambart,
19

-prunezdecydowanie działa i jest najlepszą odpowiedzią, ponieważ zapobiega zejściu do katalogu, który chcesz wykluczyć. -not -pathktóry nadal przeszukuje wykluczony katalog, po prostu nie drukuje wyniku, co może być problemem, jeśli wyłączonym katalogiem jest podłączony wolumin sieciowy lub użytkownik nie ma uprawnień.

Część trudna polega na tym, że findjest bardzo szczególna w kolejności argumentów, więc jeśli nie zrozumiesz ich poprawnie, twoje polecenie może nie działać. Kolejność argumentów jest na ogół taka:

find {path} {options} {action}

{path}: Najpierw umieść wszystkie argumenty związane ze ścieżką, np . -path './dir1' -prune -o

{options}: Odnoszę największy sukces, gdy stawiam -name, -iname, etcjako ostatnią opcję w tej grupie. Na przykład-type f -iname '*.js'

{action}: Będziesz chciał dodać -printpodczas korzystania-prune

Oto działający przykład:

# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js

# search for *.js, exclude dir1
find . -path './dir1' -prune -o -type f -iname '*.js' -print

# search for *.js, exclude dir1 and dir2
find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print
wisbucky
źródło
16

To jest format, którego użyłem do wykluczenia niektórych ścieżek:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

Użyłem tego, aby znaleźć wszystkie pliki spoza ścieżek „. *”:

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
użytkownik1882879
źródło
Próbowałem tego i nadal schodzi do katalogów, więc prędkość zdecydowanie nie jest poprawiona.
Br.Bill
10

Podejście -path -prune działa również z symbolami wieloznacznymi na ścieżce. Oto instrukcja find, która znajdzie katalogi dla serwera git obsługującego wiele repozytoriów git z pominięciem wewnętrznych katalogów git:

find . -type d \
   -not \( -path */objects -prune \) \
   -not \( -path */branches -prune \) \
   -not \( -path */refs -prune \) \
   -not \( -path */logs -prune \) \
   -not \( -path */.git -prune \) \
   -not \( -path */info -prune \) \
   -not \( -path */hooks -prune \)  
Wolfgang Fahl
źródło
9

Aby wykluczyć wiele katalogów:

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)

Aby dodać katalogi, dodaj -o -path "./dirname/*":

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)

Ale może powinieneś użyć wyrażenia regularnego , jeśli istnieje wiele katalogów do wykluczenia.

JBENOIT
źródło
9

Jest wiele dobrych odpowiedzi, zajęło mi trochę czasu, aby zrozumieć, do czego służy każdy element polecenia i jaka jest logika.

find . -path ./misc -prune -o -name '*.txt' -print

find rozpocznie wyszukiwanie plików i katalogów w bieżącym katalogu, stąd find ..

-oOpcja oznacza logiczną OR i oddziela dwie części komendy:

[ -path ./misc -prune ] OR [ -name '*.txt' -print ]

Każdy katalog lub plik, który nie jest katalogiem ./misc, nie przejdzie pierwszego testu -path ./misc. Ale zostaną przetestowane pod kątem drugiego wyrażenia. Jeśli ich nazwa odpowiada wzorowi *.txt, drukowane są z powodu -printopcji.

Gdy find osiągnie katalog ./misc, katalog ten spełnia tylko pierwsze wyrażenie. Tak więc -pruneopcja zostanie zastosowana do niego. Mówi poleceniu find, aby nie eksplorowało tego katalogu. Tak więc żaden plik lub katalog w ./misc nie będzie nawet eksplorowany przez find, nie będzie testowany względem drugiej części wyrażenia i nie zostanie wydrukowany.

Istopopoki
źródło
Każdy ma rozwiązanie, ale twoje najlepiej to wyjaśniło. Byłem nieugięty, żeby najpierw używać -name zamiast -path. Twoje wyjaśnienie było odpowiednie, aby dojść do tego, czego chciałem. odnaleźć . -nazwa „* .txt” -print -o -path ./misc -prune
Vendetta V
7

Dla działającego rozwiązania (testowane na Ubuntu 12.04 (Precise Pangolin)) ...

find ! -path "dir1" -iname "*.mp3"

wyszuka pliki MP3 w bieżącym folderze i podfolderach, z wyjątkiem podfolderu dir1.

Posługiwać się:

find ! -path "dir1" ! -path "dir2" -iname "*.mp3"

... aby wykluczyć katalog 1 i katalog 2

James Dupin
źródło
Nie działa dla mnie. Nie wykonuj też żadnej z powyższych odpowiedzi. Czerwony kapelusz.
Tharpa
6

dobrą sztuczką pozwalającą uniknąć drukowania przyciętych katalogów jest użycie -print(działa -execrównież) po prawej stronie -orpo -prune. Na przykład, ...

find . -path "*/.*" -prune -or -iname "*.j2"

wydrukuje ścieżkę wszystkich plików w bieżącym katalogu z rozszerzeniem `.j2", pomijając wszystkie ukryte katalogi. Schludnie. Ale wydrukuje również wydruk pełną ścieżkę każdego katalogu, który pomija, jak wspomniano powyżej. Jednak następujące nie, ...

find . -path "*/.*" -prune -or -iname "*.j2" -print

ponieważ logicznie jest ukryte -andza -inameoperatorem i przed nadrukiem. To wiąże go z prawą częścią -orzdania ze względu na logiczną kolejność operacji i asocjatywność. Ale doktorzy mówią, że jest ukryty, -printjeśli on (lub którykolwiek z jego kuzynów ... -print0itd.) Nie jest określony. Dlaczego więc nie lewa część -orwydruku? Najwyraźniej (i nie zrozumiałem tego po pierwszym przeczytaniu strony podręcznika), to prawda, jeśli nie ma nic, tylko operatory opisowe najwyraźniej nic nie zrobiłyby, więc myślę, że ma to sens. Jak wspomniano powyżej, to wszystko również działa , więc poniższe zawiera pełną listę każdego pliku z pożądanym rozszerzeniem, ale nie wyświetla pierwszego poziomu każdego ukrytego katalogu, ...-print operacji w stylu -lub wyrażonej w dowolnej klauzuli, wszystkie te ukryte logiczne znikają i dostajesz tylko to, co ty sprecyzować. Szczerze mówiąc, wolałbym to na odwrót, ale potem-exec gdziekolwiek, w którym to przypadku, -print jest logicznie traktowane wokół tak, że wszystko zostanie wydrukowany. Jeśli nawet JEDENprintfind-execls -la

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +

Dla mnie (i innych w tym wątku) findskładnia robi się dość barokowa dość szybko, więc zawsze rzucam pareny, aby upewnić się, że wiem, co się z tym wiąże, więc zwykle tworzę makro dla zdolności pisania i formuję wszystkie takie stwierdzenia jak. ..

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)

Trudno się pomylić, ustanawiając świat na dwie części w ten sposób. Mam nadzieję, że to pomaga, choć wydaje się mało prawdopodobne, aby ktokolwiek przeczytał ponad 30. odpowiedź i zagłosował, ale można mieć nadzieję. :-)

cykliny
źródło
5

Aby to zrobić, możesz użyć opcji przycinania. Jak na przykład:

find ./ -path ./beta/* -prune -o -iname example.com -print

Lub odwrotna opcja grep „grep -v”:

find -iname example.com | grep -v beta

Możesz znaleźć szczegółowe instrukcje i przykłady w Linuxie polecenie find wyklucza wyszukiwanie katalogów .

Siju V.
źródło
Rozwiązanie grep jest jedynym, które wyklucza wszystkie katalogi o tej samej nazwie. Przy próbie wykluczenia „node_modules” jest to całkiem przydatne.
bmacnaughton
3
@bmacnaughton - nieprawda! Przybyłem tu specjalnie po to, aby wykluczyć „node_modules” i po przeczytaniu wielu dobrych odpowiedzi zdecydowałem się na find . -type f -print -o -path "*/node_modules" -prune... używając znaku wieloznacznego pomija „node_modules” na dowolnym poziomie; użycie -printpierwszej alternatywy -type f -printpowoduje wydrukowanie tylko tej części, więc same katalogi „node_modules” nie są wymienione. (może być również odwrotna: find . -path "*/node_modules" -prune -o -type f -print)
Stephen P
co * tam robi. Jaki dokładnie plik chcesz wykluczyć. Czy używasz go jako symbolu wieloznacznego?
Siju V
1
@StephenP, dzięki za zwrócenie na to uwagi; Nauczyłem się różnicy między używaniem ./node_modulesa */node_modulestym. W moim przypadku, node_modulesgdy istnieje tylko w katalogu, w którym rozpoczynam wyszukiwanie (i w tym node_moduleskatalogu), mogę użyć, find . -type f -print -o -path "./node_modules" -prune ponieważ nie będzie node_moduleskatalogu w żadnym innym katalogu.
bmacnaughton
1
@SijuV - w katalogu, w którym szukałem, był node_modulespodkatalog, ale były też podkatalogi, które miały swoje własne moduły_węzłów ... używając ./node_modulesdopasowań tylko podkatalogu node_modulesw bieżącym katalogu .i przycinają go; używając */node_modulesdopasowuje i przycina katalog na dowolnej głębokości, ponieważ *jako glob pasuje do dowolnego wiodącego prefiksu ścieżki, takiego jak ./test5/main/node_modulesnie tylko ./prefiks. *Jest wieloznaczny, ale jako glob nie jako regex.
Stephen P.
5
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
Archetypalny Paweł
źródło
Nie mogę tego zrobić. find ~/Projects -name '*.js' -\! -name 'node_modules' -prunewciąż wyświetla pliki node_modulesna swojej ścieżce
mpen
1
@mpen, Od stackoverflow.com/questions/4210042/… , dowiedziałem się, że potrzebna jest składnia find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Nazwa tej ścieżki musi dokładnie pasować do tego, co znajdzie, co wydrukowałoby, gdyby miał wydrukować katalog.
PatS
4
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

wydaje się działać tak samo jak

find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)

i łatwiej zapamiętać IMO.

Funkodebat
źródło
4

TLDR: zrozum katalogi główne i dostosuj wyszukiwanie z tego miejsca, korzystając z tej -path <excluded_path> -prune -oopcji. Nie dołączaj końcowego/ końca na końcu wykluczonej ścieżki.

Przykład:

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


Aby skutecznie korzystać z find, uważam, że konieczne jest dobre zrozumienie struktury katalogów systemu plików. Na moim komputerze domowym mam dyski twarde o pojemności TB, z kopią zapasową około połowy tej zawartości przy użyciu rsnapshot(tj rsync.). Chociaż tworzy kopię zapasową na fizycznie niezależnym (zduplikowanym) dysku, jest on montowany w katalogu systemowym root ( /) /mnt/Backups/rsnapshot_backups/:

/mnt/Backups/
└── rsnapshot_backups/
    ├── hourly.0/
    ├── hourly.1/
    ├── ...
    ├── daily.0/
    ├── daily.1/
    ├── ...
    ├── weekly.0/
    ├── weekly.1/
    ├── ...
    ├── monthly.0/
    ├── monthly.1/
    └── ...

/mnt/Backups/rsnapshot_backups/Katalog zajmuje obecnie ~ 2,9 TB, z ~ 60m plików i folderów; po prostu przeglądanie tych treści wymaga czasu:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find /mnt/Backups/rsnapshot_backups | wc -l
60314138    ## 60.3M files, folders
34:07.30    ## 34 min

time du /mnt/Backups/rsnapshot_backups -d 0
3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
33:51.88    ## 34 min

time rsnapshot du    ## << more accurate re: rsnapshot footprint
2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
...
4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
2:34:54          ## 2 hr 35 min

Dlatego za każdym razem, gdy muszę wyszukać plik na mojej /(głównej) partycji, muszę poradzić sobie (jeśli to możliwe) przechodząc przez moją partycję kopii zapasowych.


PRZYKŁADY

Wśród podejść, które są różnie sugerowane w tym wątku ( Jak wykluczyć katalog w poleceniu find. ), Stwierdzam, że wyszukiwania przy użyciu zaakceptowanej odpowiedzi są znacznie szybsze - z zastrzeżeniami.

Rozwiązanie 1

Powiedzmy, że chcę znaleźć plik systemowy libname-server-2.a, ale nie chcę przeszukiwać rsnapshotkopii zapasowych. Aby szybko znaleźć plik systemowy, użyj wykluczyć ścieżkę /mnt(czyli zastosowanie /mnt, nie /mnt/, albo /mnt/Backups, albo ...):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
real    0m8.644s              ## 8.6 sec  <<< NOTE!
user    0m1.669s
 sys    0m2.466s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 3 sec     ## ~3 sec  <<< NOTE!

... znajduje ten plik w ciągu kilku sekund, a to trwa znacznie dłużej (wydaje się, że powtarza się we wszystkich „wykluczonych” katalogach):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
find: warning: -path /mnt/ will not match anything because it ends with /.
/usr/lib/libname-server-2.a
real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
user    1m43.142s
 sys    2m22.666s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 1775 sec    ## 29.6 min

Rozwiązanie 2

Inne rozwiązanie oferowane w tym wątku ( SO # 4210042 ) również działa słabo:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -name "*libname-server-2.a*" -not -path "/mnt"
/usr/lib/libname-server-2.a
real    33m37.911s            ## 33 min 38 sec (~235x slower)
user    1m45.134s
 sys    2m31.846s

time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
/usr/lib/libname-server-2.a
real    33m11.208s            ## 33 min 11 sec
user    1m22.185s
 sys    2m29.962s

PODSUMOWANIE | WNIOSKI

Zastosuj podejście przedstawione w „ Rozwiązaniu 1

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print

to znaczy

... -path <excluded_path> -prune -o ...

zauważając, że za każdym razem, gdy dodajesz znak końcowy /do wykluczonej ścieżki, findpolecenie rekurencyjnie wchodzi (wszystkie te) /mnt/*katalogi - które w moim przypadku, z powodu /mnt/Backups/rsnapshot_backups/*podkatalogów, zawierają dodatkowo ~ 2,9 TB plików do przeszukania! Nie dołączając znaku /końca, wyszukiwanie powinno zakończyć się niemal natychmiast (w ciągu kilku sekund).

... -not -path <exclude path> ...Wydaje się, że „Rozwiązanie 2” ( ) rekurencyjnie przeszukuje wykluczone katalogi - nie zwraca wykluczonych dopasowań, ale niepotrzebnie pochłania ten czas wyszukiwania.


Wyszukiwanie w ramach tych rsnapshotkopii zapasowych:

Aby znaleźć plik w jednej z moich godzinnych / dziennych / tygodniowych / miesięcznych rsnapshotkopii zapasowych):

$ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                            ## (~4 GB), it is in fact searching through ~2.9 TB)

Z wyłączeniem zagnieżdżonego katalogu:

Tutaj chcę wykluczyć katalog zagnieżdżony, np. /mnt/Vancouver/projects/ie/claws/data/*Podczas wyszukiwania z /mnt/Vancouver/projects/:

$ time find . -iname '*test_file*'
./ie/claws/data/test_file
./ie/claws/test_file
0:01.97

$ time find . -path '*/data' -prune -o -iname '*test_file*' -print
./ie/claws/test_file
0:00.07

Na bok: Dodanie -printna końcu polecenia pomija wydruk wykluczonego katalogu:

$ find / -path /mnt -prune -o -name "*libname-server-2.a*"
/mnt
/usr/lib/libname-server-2.a

$ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
Victoria Stuart
źródło
To nie wielkość plików spowalnia find, to liczba pozycji katalogu, które musi zbadać. Jest więc znacznie gorzej, jeśli masz wiele, wiele małych plików (zwłaszcza jeśli wszystkie są wielokrotnie połączone!) Niż jeśli masz tylko garść plików o pojemności wielu gigabajtów.
Toby Speight
@TobySpeight: dobry punkt. Wspomniałem o wielkości przestrzeni wyszukiwania, aby wskazać skalę, która zawiera również wiele plików. Szybkie wyszukiwanie katalogu głównego (/) sudo ls -R / | wc -lwskazuje ~ 76,5 mln plików (większość z nich jest archiwizowana, z wyjątkiem plików systemowych innych niż config); /mnt/Vancouver/z ls -R | wc -loznacza ~ 2,35 mln plików; /home/victoria/zawiera pliki 0.668M.
Victoria Stuart,
4

Możesz także użyć wyrażeń regularnych, aby włączyć / wyłączyć niektóre pliki / katalogi wyszukiwania, używając czegoś takiego:

find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 

To da ci tylko wszystkie pliki js, vue, css itp., Ale z wyłączeniem wszystkich plików w folderach node_modulesi vendor.

supersan
źródło
3

Użyłem, findaby dostarczyć listę plików xgettexti chciałem pominąć konkretny katalog i jego zawartość. Próbowałem wielu -pathkombinacji w połączeniu z, -pruneale nie byłem w stanie w pełni wykluczyć katalogu, który chciałem zniknąć.

Chociaż byłem w stanie zignorować zawartość katalogu, który chciałem zignorować, zwróciłem findsam katalog jako jeden z wyników, co spowodowałoxgettext awarię w wyniku (nie akceptuje katalogów, tylko pliki).

Moim rozwiązaniem było po prostu grep -vpominięcie katalogu, którego nie chciałem w wynikach:

find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext

Bez względu na to, czy istnieje argument przemawiający za findtym, czy będzie on działał w 100%, nie mogę powiedzieć na pewno. Korzystanie grepbyło szybkim i łatwym rozwiązaniem po pewnym bólu głowy.

Lemmings19
źródło
3

Żadna z poprzednich odpowiedzi nie jest dobra na Ubuntu. Spróbuj tego:

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"

Znalazłem to tutaj

Sixro
źródło
Nie widzę powodu, dla którego żadna z odpowiedzi z więcej niż 100 punktami nie powinna działać na Ubuntu.
Axel Beckert
mmm, zobaczmy? może dlatego, że wypróbowałem je wszystkie?
Sixro
find jest wszędzie tą samą implementacją we wszystkich dystrybucjach Linuksa - tą z Projektu GNU. Jedyną różnicą mogą być wersje. Ale zmiany w ostatniej dekadzie nie były tak inwazyjne, z wyjątkiem może dopasowania uprawnień.
Axel Beckert
3

Jest to odpowiednie dla mnie na komputerze Mac:

find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune

Wyklucza vendori app/cachereżimem szukanej nazwy z przyrostkiem php.

Jiahut
źródło
Lepiej wstaw pojedyncze cudzysłowy wokół „* .php”, inaczej nie znajdziesz tego, czego szukasz.
Br.Bill
3

Dla tych z was w starszych wersjach UNIX, którzy nie mogą używać -path lub -not

Testowane na SunOS 5.10 bash 3.2 i SunOS 5.11 bash 4.4

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f
JaredTS486
źródło
Można przekazać więcej niż określony katalog.
MUY Belgia,
2

how-to-use-prune-option-of-find-in-sh to doskonała odpowiedź Laurence Gonsalves na temat tego, jak -prunedziała.

A oto ogólne rozwiązanie:

find /path/to/search                    \
  -type d                               \
    \( -path /path/to/search/exclude_me \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print

Aby uniknąć /path/to/seach/wielokrotnego pisania , zawiń findw pushd .. popdparę.

pushd /path/to/search;                  \
find .                                  \
  -type d                               \
    \( -path ./exclude_me               \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print;         \
 popd
go2null
źródło
1
Z stackoverflow.com/questions/4210042 /... dowiedziałem się, że składnia użyta dla parametru -pathmust pasuje do znalezionej nazwy, która zostałaby wydrukowana, gdyby wydrukowała katalog, na przykład find . -path ./.git -prune -o -print, lub find $HOME/foo -path $HOME/foo/.git -prune -o -print niektóre odpowiedzi po prostu mówią, -path somedirktóra niestety jest niewystarczająco dokładne, aby były przydatne.
PatS
2

Dla tego, czego potrzebowałem, działało to tak: znajdowanie landscape.jpgna wszystkich serwerach zaczynających się od katalogu głównego i wykluczanie wyszukiwania w /varkatalogu:

find / -maxdepth 1 -type d | grep -v /var | xargs -I '{}' find '{}' -name landscape.jpg

find / -maxdepth 1 -type dwymienia wszystkie d irectories w/

grep -v /var wyklucza `/ var 'z listy

xargs -I '{}' find '{}' -name landscape.jpgwykonaj dowolne polecenie, na przykład finddla każdego katalogu / wyniku z listy

adrianTNT
źródło
Poczekaj chwilę, /nie jest jeszcze wykluczone. Możesz potrzebować sed 1d.
Simba,
2

Działa następujące polecenie:

find . -path ./.git -prune -o -print

Jeśli masz problem ze znalezieniem, użyj -D treeopcji, aby wyświetlić informacje analizy wyrażeń.

find -D tree . -path ./.git -prune -o -print

Lub -D all, aby wyświetlić wszystkie informacje o wykonaniu.

find -D all . -path ./.git -prune -o -print
EIIPII
źródło
1

Znalazłem nazwę funkcji w plikach źródłowych C wykluczających * .o i wykluczających * .swp i wykluczających (nie zwykły plik) i wykluczających dane wyjściowe katalogu za pomocą tego polecenia:

find .  \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach
Iwan Iwanowicz
źródło
1

Lepiej użyj execakcji niż forpętli:

find . -path "./dirtoexclude" -prune \
    -o -exec java -jar config/yuicompressor-2.4.2.jar --type js '{}' -o '{}' \;

exec ... '{}' ... '{}' \;Będzie wykonywana raz dla każdego pliku pasującego, zastępując szelki '{}'z aktualną nazwą pliku.

Zauważ, że nawiasy klamrowe są ujęte w pojedyncze znaki cudzysłowu, aby chronić je przed interpretacją jako interpunkcji skryptu powłoki * .


Notatki

* W sekcji Przykłady find (GNU findutils) 4.4.2manualu

Alberto
źródło
1
Bardzo stare pytanie, ale wciąż jest miejsce na ulepszenia. Znalazłem to przypadkiem, próbując rozwiązać podobny problem i żadna z odpowiedzi nie była zadowalająca.
Alberto,
execCzęsto używam tej akcji i uważam ją za bardzo przydatną. Zazwyczaj dodaję cudzysłowy, {}jeśli w ścieżkach plików są spacje, co daje "{}".
Ludovic Kuty,
@lkuty Miałem właśnie edytować mój post, aby odzwierciedlić twój komentarz, ale po szybkim teście (bez cytowania {}działa w przypadku plików z białymi spacjami w nazwach) i przejrzeniu stron podręcznika, wydaje się, że cytowanie jest konieczne, aby uniknąć są błędnie interpretowane jako interpunkcja skryptu powłoki. W takim przypadku użyłbyś pojedynczego cudzysłowu:'{}'
Alberto
Myślę, że musiałem go użyć do zrobienia cplub mvlub rm. Sprawdzę to
Ludovic Kuty
1

Próbowałem polecenia powyżej, ale żaden z tych, którzy używają „-prune” nie działa dla mnie. W końcu wypróbowałem to za pomocą poniższego polecenia:

find . \( -name "*" \) -prune -a ! -name "directory"
Jolly Liu
źródło