So schließen Sie ein Verzeichnis in find aus. Befehl

1380

Ich versuche, einen findBefehl für alle JavaScript-Dateien auszuführen , aber wie schließe ich ein bestimmtes Verzeichnis aus?

Hier ist der findCode, den wir verwenden.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
helion3
quelle
10
Welches Verzeichnis müssen Sie ausschließen?
Der archetypische Paul
11
Es ist besser zu benutzen find ... | while read -r file .... Es ist auch besser, Antworten zu akzeptieren und zu bewerten.
Bis auf weiteres angehalten.
während das Lesen langsam ist, denn in ist schneller
mpapis
18
@mpapis behandelt beim korrekten Lesen vollständige Zeilen mit Leerzeichen.
Jean-Philippe Pellet
1
Führen Sie dies einfach in einem Ordner mit Dateien aus, deren Namen Leerzeichen enthalten : for file in $(find .); do echo "$file"; done. Namen mit Leerzeichen werden geteilt, was wir nicht wollen.
Jean-Philippe Pellet

Antworten:

1140

Verwenden Sie den -pruneSchalter. Wenn Sie beispielsweise das miscVerzeichnis ausschließen möchten, fügen Sie -path ./misc -prune -oIhrem Suchbefehl einfach ein hinzu :

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

Hier ist ein Beispiel mit mehreren Verzeichnissen:

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

Hier schließen wir dir1 , dir2 und dir3 aus , da es sich bei findAusdrücken um eine Aktion handelt, die auf die Kriterien -path dir1 -o -path dir2 -o -path dir3(wenn dir1 oder dir2 oder dir3 ) einwirkt , UNDed mit type -d.

Weitere Aktion ist -o print, einfach drucken.

f10bit
quelle
89
Hmm. Dies funktioniert auch bei mir nicht, da das ignorierte Verzeichnis "./misc" in die Ausgabe aufgenommen wird.
Theuni
84
@ Theuni Es hat wahrscheinlich nicht für Sie funktioniert, weil Sie nicht -printexplizit eine (oder eine andere Aktion) hinzugefügt haben -name. In diesem Fall werden beide "Seiten" -ogedruckt, während bei Verwendung -printnur diese Seite gedruckt wird.
Daniel C. Sobral
4
Von der Manpage: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Wie gehe ich beim Löschen mit find vor, wenn ich bestimmte Verzeichnisse vom Löschen ausschließen möchte?
Jānis Elmeris
15
Um das gesamte Verzeichnis selbst aus den Ergebnissen zu entfernen, verwenden Sie : find . -not -path "./.git*". Wenn Sie ./dir*anstelle von verwenden, werden sowohl ./dir/*das Verzeichnis als auch der Inhalt aus der Ausgabe entfernt.
Micahblu
64
Diese Frage und die Verwirrung in den Antworten zeigen, wie schlecht die Benutzeroberfläche von find mit den Anforderungen der Benutzer übereinstimmt.
Johannes Overmann
1931

Wenn -prunees bei Ihnen nicht funktioniert, wird dies:

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

Vorsichtsmaßnahme: Erfordert das Durchlaufen aller unerwünschten Verzeichnisse.

GetFree
quelle
86
Einer der Kommentare in der akzeptierten Antwort weist auf das Problem hin. -pruneschließt das Verzeichnis selbst nicht aus, es schließt seinen Inhalt aus, was bedeutet, dass Sie eine unerwünschte Zeile in der Ausgabe mit dem ausgeschlossenen Verzeichnis erhalten.
GetFree
96
Gute Antwort. Ich möchte hinzufügen, dass Sie ein Verzeichnis auf JEDER Ebene ausschließen können, indem Sie das erste .in ändern *. so find -name "*.js" -not -path "*/omitme/*"würde Dateien aus einem Verzeichnis mit dem Namen „omitme“ auf jeder Ebene der Tiefe wegzulassen.
DeeDee
83
Es durchläuft jedoch immer noch das gesamte unerwünschte Verzeichnis. Ich füge meine eigene Antwort hinzu. :-)
Daniel C. Sobral
18
Beachten Sie jedoch, dass die Bereinigungsoption nur dann nicht funktioniert, wenn Sie sie nicht -printexplizit verwenden.
Daniel C. Sobral
39
Es wäre besser zu sagen "Dies ist eine Alternative zur Verwendung von -prune". Die Antworten, die -prune vorschlagen, sind eindeutig nicht falsch, sie sind einfach nicht so, wie Sie es tun würden.
Jimbo
458

Ich finde Folgendes leichter zu überlegen als andere vorgeschlagene Lösungen:

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

Wichtiger Hinweis: Die Pfade, nach denen Sie eingeben, -pathmüssen genau mit denen übereinstimmen, finddie ohne Ausschluss gedruckt werden. Wenn dieser Satz verwirrt, stellen Sie einfach sicher, dass Sie den gesamten Befehl wie folgt vollständig verwenden : . Siehe Anmerkung [1], wenn Sie ein besseres Verständnis wünschen.find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

Im Inneren \(und \)ist ein Ausdruck, der genau übereinstimmt build/external(siehe wichtigen Hinweis oben) und bei Erfolg verhindert, dass etwas weiter unten durchlaufen wird . Dies wird dann als einzelner Ausdruck mit der maskierten Klammer gruppiert und mit -notdem Präfix versehen, wodurch findalles übersprungen wird, was mit diesem Ausdruck übereinstimmt.

Man könnte fragen, ob durch das Hinzufügen -notnicht alle anderen Dateien durch -pruneerneutes Erscheinen ausgeblendet werden , und die Antwort lautet Nein. Die Art und Weise -prunefunktioniert so, dass alles, was, sobald es erreicht ist, die Dateien unter diesem Verzeichnis dauerhaft ignoriert werden.

Dies kommt aus einem tatsächlichen Anwendungsfall, in dem ich yui-compressor für einige von wintersmith generierte Dateien aufrufen musste, aber andere Dateien, die so wie sie sind gesendet werden müssen, weglassen musste.


Hinweis [1] : Wenn Sie ausschließen möchten /tmp/foo/barund find wie folgt ausführen, find /tmp \(...müssen Sie angeben -path /tmp/foo/bar. Wenn Sie andererseits find so ausführen, cd /tmp; find . \(...müssen Sie angeben -path ./foo/bar.

Daniel C. Sobral
quelle
37
Hervorragende Antwort, danke. Dies funktioniert und ist für mehrere Ausschlüsse skalierbar (lesbar). Sie sind ein Gentlemen und ein Gelehrter, Sir. Vielen Dank für das Beispiel für mehrere Ausschlüsse
Freedom_Ben
7
Dies funktioniert nicht, wenn ich -delete switch verwenden möchte: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 Sie können -exec rm -rf {} \;anstelle von verwenden -delete.
Daniel C. Sobral
11
Wenn man die Ausgabe von untersucht find, ist das wirklich offensichtlich, aber es hat mich gestolpert. Wenn Sie im aktuellen Verzeichnis suchen (indem Sie .als Suchpfad angeben oder gar keinen angeben), möchten Sie höchstwahrscheinlich, dass Ihr Muster danach -pathbeginnt ./, z find -not \( -path ./.git -prune \) -type f. B.: .
Zantier
7
Eine präzisere (und POSIX-kompatible) Variante dieser Methode: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)gefolgt von allen Bedingungen, die Ihren Anforderungen entsprechen sollten.
Walf
218

Hier besteht eindeutig Verwirrung darüber, wie die bevorzugte Syntax zum Überspringen eines Verzeichnisses lauten soll.

GNU Stellungnahme

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

Auf der GNU finden Sie eine Manpage

Argumentation

-prunestoppt den findAbstieg in ein Verzeichnis. Nur die Angabe -not -pathwird weiterhin in das übersprungene Verzeichnis verschoben , ist jedoch -not -pathbei jedem findTest jeder Datei falsch .

Probleme mit -prune

-prune tut, was es beabsichtigt, aber es gibt immer noch einige Dinge, auf die Sie achten müssen, wenn Sie es verwenden.

  1. find druckt das beschnittene Verzeichnis.

    • WAHR Das ist beabsichtigtes Verhalten, es steigt einfach nicht hinein. Verwenden Sie eine Syntax, die das Verzeichnis logisch weglässt, um zu vermeiden, dass das Verzeichnis vollständig gedruckt wird.
  2. -prunefunktioniert nur mit -printund keine anderen Aktionen.

    • NICHT WAHR . -prunefunktioniert mit jeder Aktion außer -delete. Warum funktioniert es nicht mit Löschen? Um -deletezu arbeiten, muss find das Verzeichnis in der DFS-Reihenfolge durchlaufen, da -deletezuerst die Blätter, dann die Eltern der Blätter usw. gelöscht werden. Damit die Angabe -prunejedoch sinnvoll ist, findmuss ein Verzeichnis getroffen und der Abstieg gestoppt werden macht offensichtlich keinen Sinn mit -depthoder -deleteauf.

Performance

Ich habe einen einfachen Test der drei am besten bewerteten Antworten auf diese Frage erstellt (ersetzt -printdurch -exec bash -c 'echo $0' {} \;, um ein anderes Aktionsbeispiel zu zeigen). Die Ergebnisse sind unten

----------------------------------------------
# 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

Fazit

Sowohl f10bit Syntax und Daniel C. Sobral Syntax nahm 10-25ms im Durchschnitt zu laufen. Die Syntax von GetFree , die nicht verwendet wird -prune, dauerte 865 ms. Ja, dies ist ein ziemlich extremes Beispiel, aber wenn Sie sich für die Laufzeit interessieren und etwas aus der Ferne intensiv tun, sollten Sie es verwenden -prune.

Hinweis Die Syntax von Daniel C. Sobral war die bessere der beiden -pruneSyntaxen. Ich vermute jedoch stark, dass dies das Ergebnis eines Zwischenspeicherns ist, da das Umschalten der Reihenfolge, in der die beiden ausgeführt wurden, zu dem entgegengesetzten Ergebnis führte, während die Version ohne Bereinigung immer am langsamsten war.

Testskript

#!/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
Setzen Sie Monica bitte wieder ein
quelle
18
Vielen Dank für eine sehr gute Analyse. In Bezug auf "Ich vermute sehr, dass dies das Ergebnis von Caching ist" können Sie diesen Befehl ausführen: sudo sh -c "free && sync && echo 3> / proc / sys / vm / drop_caches && free", um den Cache zu löschen (siehe Unix. stackexchange.com/questions/87908/… ).
Ndemou
Nach ein paar Tests an diesen beiden mit -prunekann ich feststellen, dass es selten einen Unterschied gibt. Denken Sie daran, dass der erste Befehlsstart von der CPU-Leistung profitiert. Der spätere Aufwärmen der CPU> Leistungsabfall führt zu einer geringfügigen Verlangsamung (ich habe den Cache vor jedem Befehl als @ ndemou-Vorschlag gelöscht)
Huy.PhamNhu
Versuchen Sie, die Nummer name1() name2() name3()im obigen @ BroSlow-Testskript zu wechseln, um die Ausführungsreihenfolge zu ändern und eine visuelle Darstellung meiner Aussagen zu erhalten. Im wirklichen Leben ist es zwischen diesen beiden jedoch nicht wahrnehmbar.
Huy.PhamNhu
Beifall. Vielen Dank für diese qualitativ hochwertige Antwort.
Stphane
Sie sollten nicht -o sein, was bedeutet oder. Sie beschneiden also im ersten Schritt und vergessen dann im nächsten Schritt alles.
mmm
97

Dies ist der einzige, der für mich gearbeitet hat.

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

Suche nach "MyFile" ohne "Directory". Betonen Sie die Sterne *.

DimiDak
quelle
13
Diese Methode funktioniert unter macOS, die akzeptierte Antwort jedoch nicht. Ich weiß, die ursprüngliche Frage ist für Linux.
Xavier Rubio Jansana
5
Beachten Sie, dass Sie ! -path '*/Directory/*'Ihrem Befehl nacheinander mehrere hinzufügen können, um mehrere Verzeichnisse zu ignorieren
Aclwitt
Funktioniert unter MacOS, aber nicht unter Linux ... bestätigt
Marcello de Sales
In einem docker containereinzigen Werk mitsh -c "find..."
Marcello de Sales
@Marcello de Sales Natürlich funktioniert es unter Linux.
DimiDak
59

Eine Möglichkeit wäre, alle Ergebnisse, die den Verzeichnisnamen enthalten, mit grep auszuschließen. Zum Beispiel:

find . -name '*.js' | grep -v excludeddir
Joshua
quelle
44
Dies wird Ihre Suche sehr langsam machen
Dorian
6
Dieser hat für mich funktioniert, andere (die verwenden -prune) - nicht.
Andron
7
Langsam bei großen Ergebnissen, aber nützlich bei kleineren Sätzen. Aber wie kann man mit grep mehrere Verzeichnisse ausschließen? Natürlich so: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3aber es kann einen Grep-Weg geben.
Timo Kähkönen
6
Wenn Sie mehrere Greps ausführen möchten, schreiben Sie sie besser als reguläre Ausdrücke : egrep -v '(dir1|dir2|dir3)'. In dieser speziellen Fallstudie ist es jedoch besser, Verzeichnisse in sich findselbst auszuschließen .
Laurence
1
Ja, und Sie benötigen keine Klammern. Verwenden Sie am besten ^, um sicherzustellen, dass der Verzeichnisname am Anfang der Zeichenfolge übereinstimmt, z. B.: find. -name '* .js' | egrep -v "^ \ ./ ausgeschlossendir1 | ^ \ ./ ausgeschlossendir2"
Sofija
41

Ich bevorzuge die -notNotation ... sie ist besser lesbar:

find . -name '*.js' -and -not -path directory
mpapis
quelle
5
Entschuldigung, es funktioniert nicht. In der Manpage für findheißt es: "Um ein Verzeichnis und die Dateien darunter zu ignorieren, verwenden Sie -prune."
Christian Davén
8
Das ist falsch. Es verhindert nicht, dass find in das Verzeichnis gelangt und alle darin enthaltenen Dateien durchläuft.
GetFree
find . -iname '*' -and -not -path './somePath'hindert es nicht daran, dieses Verzeichnis zu betreten.
Lemmings19
Dies half mir mit .git Pfad find . -iname '*' -not -path './.git/*'
Mark Shust an der M.academy
7
@rane: Genauer gesagt find . -not -path "*/.git*"wäre das, was du willst.
Ben
20

Verwenden Sie die Option -prune. Also so etwas wie:

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

Der '-type d -name proc -prune' sucht nur nach Verzeichnissen mit dem Namen proc, die ausgeschlossen werden sollen.
Das '-o' ist ein 'ODER'-Operator.

Drew Frezell
quelle
1
Dies ist die einzige reine "Find" -Lösung, die für mich funktioniert hat. Die Verzeichnisse, die ich ausschließen wollte, befinden sich NICHT unmittelbar unter dem aktuellen Arbeitsverzeichnis.
Lambart
5
Das Hinzufügen -printzum Ende kann jedoch die Ergebnisse verbessern. find . -type d -name .hg -prune -o -name dataignorierte den Inhalt der (mehreren) .hgVerzeichnisse, listete jedoch die .hgVerzeichnisse selbst auf. Mit -printwurden nur die von mir gesuchten "Daten" -Verzeichnisse aufgelistet.
Lambart
19

-prunefunktioniert definitiv und ist die beste Antwort, da es verhindert, dass Sie in das Verzeichnis absteigen, das Sie ausschließen möchten. -not -pathWenn das ausgeschlossene Verzeichnis weiterhin durchsucht wird, wird das Ergebnis einfach nicht gedruckt. Dies kann ein Problem sein, wenn das ausgeschlossene Verzeichnis über ein Netzwerk-Volume bereitgestellt wird oder Sie keine Berechtigungen haben.

Der schwierige Teil ist, dass finddie Reihenfolge der Argumente sehr genau ist. Wenn Sie sie also nicht genau richtig verstehen, funktioniert Ihr Befehl möglicherweise nicht. Die Reihenfolge der Argumente ist im Allgemeinen wie folgt:

find {path} {options} {action}

{path}: Stellen Sie alle pfadbezogenen Argumente an die erste Stelle, z . -path './dir1' -prune -o

{options}: Ich habe den größten Erfolg, wenn ich -name, -iname, etcals letzte Option in diese Gruppe setze. Z.B-type f -iname '*.js'

{action}: Sie möchten hinzufügen, -printwenn Sie verwenden-prune

Hier ist ein Arbeitsbeispiel:

# 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
quelle
16

In diesem Format habe ich einige Pfade ausgeschlossen:

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

Ich habe dies verwendet, um alle Dateien zu finden, die sich nicht in ". *" - Pfaden befinden:

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
user1882879
quelle
Ich habe es versucht und es steigt immer noch in die Verzeichnisse ab, so dass die Geschwindigkeit definitiv nicht verbessert wird.
Br.Bill
10

Der Ansatz -path -prune funktioniert auch mit Platzhaltern im Pfad. Hier ist eine find-Anweisung, die die Verzeichnisse für einen Git-Server findet, der mehrere Git-Repositorys bedient, wobei die internen Git-Verzeichnisse weggelassen werden:

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
quelle
9

So schließen Sie mehrere Verzeichnisse aus:

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

Fügen Sie zum Hinzufügen von Verzeichnissen Folgendes hinzu -o -path "./dirname/*":

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

Aber vielleicht sollten Sie einen regulären Ausdruck verwenden , wenn viele Verzeichnisse ausgeschlossen werden müssen.

JBENOIT
quelle
9

Es gibt viele gute Antworten. Ich habe nur einige Zeit gebraucht, um zu verstehen, wofür jedes Element des Befehls gedacht war und welche Logik dahinter steckt.

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

find beginnt mit der Suche nach Dateien und Verzeichnissen im aktuellen Verzeichnis, daher die find ..

Die -oOption steht für ein logisches ODER und trennt die beiden Teile des Befehls:

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

Ein Verzeichnis oder eine Datei, die nicht das Verzeichnis ./misc ist, besteht den ersten Test nicht -path ./misc. Sie werden jedoch gegen den zweiten Ausdruck getestet. Wenn ihr Name dem Muster entspricht, werden *.txtsie aufgrund der -printOption gedruckt .

Wenn find das Verzeichnis ./misc erreicht, erfüllt dieses Verzeichnis nur den ersten Ausdruck. Die -pruneOption wird also darauf angewendet. Der Befehl find wird angewiesen , dieses Verzeichnis nicht zu durchsuchen. Daher werden Dateien oder Verzeichnisse in ./misc nicht einmal von find durchsucht, nicht gegen den zweiten Teil des Ausdrucks getestet und nicht gedruckt.

Istopopoki
quelle
Jeder hat eine Lösung, aber Ihre hat es am besten erklärt. Ich war fest davon überzeugt, dass -name zuerst verwendet werden sollte und nicht -path. Ihre Erklärung war ausreichend, um zu dem zu gelangen, was ich wollte. finden . -name "* .txt" -print -o -path ./misc -prune
Vendetta V
7

Für eine funktionierende Lösung (getestet unter Ubuntu 12.04 (Precise Pangolin)) ...

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

sucht im aktuellen Ordner und in den Unterordnern nach MP3-Dateien, außer im Unterordner dir1.

Verwenden:

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

... um dir1 UND dir2 auszuschließen

James Dupin
quelle
Funktioniert bei mir nicht Keine der oben genannten Antworten. Roter Hut.
Tharpa
6

Ein guter Trick, um das Drucken der beschnittenen Verzeichnisse zu vermeiden, ist die Verwendung -print(funktioniert auch für -exec) nach der rechten Seite des -orNachher -prune. Beispielsweise, ...

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

Gibt den Pfad aller Dateien unter dem aktuellen Verzeichnis mit der Erweiterung ".j2" aus und überspringt alle versteckten Verzeichnisse. Ordentlich. Es wird jedoch auch der vollständige Pfad jedes Verzeichnisses gedruckt, das übersprungen wird, wie oben angegeben folgendes nicht, ...

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

denn logischerweise gibt es ein verstecktes -andnach dem -inameOperator und vor dem -print. Dies bindet es -oraufgrund der booleschen Reihenfolge der Operationen und der Assoziativität an den rechten Teil der Klausel. Aber die Dokumente sagen, dass es ein verstecktes gibt, -printwenn es (oder einer seiner Cousins ​​... -print0usw.) nicht angegeben ist. Warum ist der linke Teil des -orDrucks nicht? Anscheinend (und das habe ich beim ersten Lesen der Manpage nicht verstanden) ist das wahr, wenn es kein -print-oder -execÜBERALL gibt. In diesem Fall wird der Druck logisch so herumgestreut, dass alles gedruckt wird. Wenn auch nur einerprint-style Operation wird in jeder Klausel ausgedrückt, alle diese versteckten logischen verschwinden und Sie erhalten nur das, was Sie angeben. Ehrlich gesagt hätte ich es vielleicht umgekehrt bevorzugt, aber dann würde ein findmit nur beschreibenden Operatoren anscheinend nichts tun, also denke ich, dass es so sinnvoll ist, wie es ist. Wie oben erwähnt, funktioniert dies alles -execauch. Im Folgenden wird eine vollständige ls -laListe für jede Datei mit der gewünschten Erweiterung angezeigt, jedoch nicht die erste Ebene jedes versteckten Verzeichnisses.

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

Für mich (und andere in diesem Thread) wird die findSyntax ziemlich schnell ziemlich barock, daher werfe ich immer Parens ein, um sicherzugehen, dass ich weiß, was an was gebunden ist. Daher erstelle ich normalerweise ein Makro für die Typfähigkeit und bilde alle Aussagen wie. ..

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

Es ist schwer, etwas falsch zu machen, wenn man die Welt auf diese Weise in zwei Teile aufteilt. Ich hoffe, das hilft, obwohl es unwahrscheinlich ist, dass jemand die über 30-jährige Antwort liest und darüber abstimmt, aber man kann hoffen. :-)

Cycolline
quelle
5

Sie können die Prune-Option verwenden, um dies zu erreichen. Wie zum Beispiel:

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

Oder die inverse grep-Option "grep -v":

find -iname example.com | grep -v beta

Detaillierte Anweisungen und Beispiele finden Sie im Linux-Befehl find. Verzeichnisse von der Suche ausschließen .

Siju V.
quelle
Die grep-Lösung ist die einzige, die alle gleichnamigen Verzeichnisse ausschließt. Wenn Sie versuchen, "node_modules" auszuschließen, ist dies sehr nützlich.
Bmacnaughton
3
@bmacnaughton - nicht wahr! Ich bin hierher gekommen, um "node_modules" auszuschließen, und nachdem ich viele gute Antworten gelesen hatte, habe ich mich find . -type f -print -o -path "*/node_modules" -pruneentschlossen ... mit dem Platzhalter "node_modules" auf jeder Ebene zu überspringen; Bei Verwendung -printder ersten Alternative -type f -printwird nur dieser Teil gedruckt, sodass die Verzeichnisse "node_modules" selbst nicht aufgelistet sind. (es kann auch umgekehrt sein: find . -path "*/node_modules" -prune -o -type f -print)
Stephen P
Was macht * / dort? Was ist die genaue Datei, die Sie ausschließen möchten. Verwenden Sie ypu als Platzhalter?
Siju V
1
@ StephenP, danke für den Hinweis; Ich habe den Unterschied zwischen ./node_modulesund */node_modulesdaraus gelernt . In meinem Fall, in dem node_modulesnur das Verzeichnis vorhanden ist, in dem ich die Suche starte (und unter diesem node_modulesVerzeichnis), kann ich verwenden, find . -type f -print -o -path "./node_modules" -prune da node_modulesunter keinem anderen Verzeichnis ein Verzeichnis vorhanden ist.
Bmacnaughton
1
@SijuV - in dem Verzeichnis , in dem ich war auf der Suche ein war node_modulesUnterverzeichnis, aber es gab auch Unterverzeichnisse , die ihr eigenes hatten node_modules ... mit ./node_modulesStreichhölzern nur das Unterverzeichnis node_modulesunter dem aktuellen Verzeichnis .und Pflaumen es; Verwenden von */node_modulesÜbereinstimmungen und Bereinigen des Verzeichnisses in einer beliebigen Tiefe, da *as als Glob mit einem führenden Pfadpräfix übereinstimmt, z. B. ./test5/main/node_modulesnicht nur mit dem ./Präfix. Das *ist ein Platzhalter, aber als Glob nicht als Regex.
Stephen P
5
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
Der archetypische Paulus
quelle
Ich kann diesen nicht zum Laufen bringen. find ~/Projects -name '*.js' -\! -name 'node_modules' -prunenode_modules
taucht
1
@mpen, Von stackoverflow.com/questions/4210042/… habe ich erfahren, dass die gewünschte Syntax lautet find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Der Name dieses Pfads muss genau mit dem übereinstimmen, der gefunden wird, wenn das Verzeichnis gedruckt wird.
PatS
4
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

scheint genauso zu funktionieren wie

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

und ist leichter zu merken IMO.

Funkodebat
quelle
4

TLDR: Verstehen Sie Ihre Stammverzeichnisse und passen Sie Ihre Suche von dort aus mithilfe der -path <excluded_path> -prune -oOption an. Fügen Sie /am Ende des ausgeschlossenen Pfads kein nachfolgendes Zeichen ein.

Beispiel:

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


Um das effektiv zu nutzen find, ist es meines Erachtens unerlässlich, ein gutes Verständnis Ihrer Dateisystem-Verzeichnisstruktur zu haben. Auf meinem Heimcomputer habe ich Festplatten mit mehreren TB, wobei etwa die Hälfte dieses Inhalts mit rsnapshot(dh rsync) gesichert wurde . Obwohl auf einem physisch unabhängigen (doppelten) Laufwerk gesichert, wird es unter meinem Systemverzeichnis root ( /) bereitgestellt /mnt/Backups/rsnapshot_backups/::

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

Das /mnt/Backups/rsnapshot_backups/Verzeichnis belegt derzeit ~ 2,9 TB mit ~ 60 Millionen Dateien und Ordnern. Das einfache Durchlaufen dieser Inhalte braucht Zeit:

## 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

Jedes Mal, wenn ich auf meiner /(Root-) Partition nach einer Datei suchen muss , muss ich mich mit dem Durchlaufen meiner Sicherungspartition befassen (wenn möglich vermeiden).


BEISPIELE

Unter den in diesem Thread unterschiedlich vorgeschlagenen Ansätzen ( So schließen Sie ein Verzeichnis im Befehl find. Aus ) finde ich, dass die Suche mit der akzeptierten Antwort viel schneller ist - mit Einschränkungen.

Lösung 1

Angenommen, ich möchte die Systemdatei finden libname-server-2.a, aber meine rsnapshotSicherungen nicht durchsuchen . Verwenden Sie den Ausschlusspfad /mnt(dh verwenden /mnt, nicht /mnt/oder /mnt/Backupsoder oder ...) , um schnell eine Systemdatei zu finden :

## 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!

... findet diese Datei in nur wenigen Sekunden, während dies viel länger dauert (scheint alle "ausgeschlossenen" Verzeichnisse zu durchlaufen):

## 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

Lösung 2

Die andere in diesem Thread angebotene Lösung ( SO # 4210042 ) weist ebenfalls eine schlechte Leistung auf:

## 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

ZUSAMMENFASSUNG | SCHLUSSFOLGERUNGEN

Verwenden Sie den in " Lösung 1 " dargestellten Ansatz.

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

dh

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

Beachten Sie, dass /der findBefehl jedes Mal , wenn Sie das Trailing zum ausgeschlossenen Pfad hinzufügen , rekursiv (alle diese) /mnt/*Verzeichnisse eingibt - was in meinem Fall aufgrund der /mnt/Backups/rsnapshot_backups/*Unterverzeichnisse zusätzlich ~ 2,9 TB an zu durchsuchenden Dateien enthält! Wenn Sie kein Trailing anhängen, /sollte die Suche fast sofort (innerhalb von Sekunden) abgeschlossen sein.

"Lösung 2" ( ... -not -path <exclude path> ...) scheint ebenfalls rekursiv durch die ausgeschlossenen Verzeichnisse zu suchen - keine ausgeschlossenen Übereinstimmungen zurückzugeben, aber diese Suchzeit unnötig zu verbrauchen.


Suchen in diesen rsnapshotBackups:

So finden Sie eine Datei in einem meiner stündlichen / täglichen / wöchentlichen / monatlichen rsnapshotBackups):

$ 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)

Ein verschachteltes Verzeichnis ausschließen:

Hier möchte ich ein verschachteltes Verzeichnis ausschließen, z. B. /mnt/Vancouver/projects/ie/claws/data/*bei der Suche aus /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

Nebenbei: Durch Hinzufügen -printam Ende des Befehls wird der Ausdruck des ausgeschlossenen Verzeichnisses unterdrückt:

$ 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
quelle
Es ist nicht die Größe der Dateien, die sich verlangsamt find, sondern die Anzahl der Verzeichniseinträge , die untersucht werden müssen. Es ist also viel schlimmer, wenn Sie viele, viele kleine Dateien haben (insbesondere wenn sie alle mehrfach verknüpft sind!), Als wenn Sie nur eine Handvoll Multi-Gigabyte-Dateien haben.
Toby Speight
@TobySpeight: guter Punkt. Ich erwähnte die Größe des Suchraums, um die Skalierung anzugeben, die auch viele Dateien enthält. Eine schnelle Suche in root (/) mit sudo ls -R / | wc -lzeigt ~ 76,5 Millionen Dateien an (von denen die meisten mit Ausnahme von "nicht konfigurierten" Systemdateien gesichert werden). /mnt/Vancouver/mit ls -R | wc -lzeigt ~ 2,35 Millionen Dateien an; /home/victoria/enthält 0,668 Millionen Dateien.
Victoria Stuart
4

Sie können auch reguläre Ausdrücke verwenden, um einige Dateien / Verzeichnisse Ihrer Suche ein- oder auszuschließen.

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

Dadurch erhalten Sie nur alle js-, vue-, css- usw. Dateien, jedoch nicht alle Dateien in den Ordnern node_modulesund vendor.

Supersan
quelle
3

Ich habe verwendet find, um eine Liste von Dateien für bereitzustellen xgettext, und wollte ein bestimmtes Verzeichnis und dessen Inhalt weglassen. Ich habe viele Permutationen von -pathkombiniert mit versucht -prune, konnte aber das Verzeichnis, das ich haben wollte, nicht vollständig ausschließen.

Obwohl ich den Inhalt des Verzeichnisses ignorieren konnte, das ich ignorieren wollte, gab ich finddas Verzeichnis selbst als eines der Ergebnisse zurück, die dazu führtenxgettext zu einem Absturz führte (akzeptiert keine Verzeichnisse; nur Dateien).

Meine Lösung bestand darin, einfach grep -vdas Verzeichnis zu überspringen, das ich in den Ergebnissen nicht haben wollte:

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

Ob es dafür ein Argument gibt find, das zu 100% funktioniert, kann ich nicht mit Sicherheit sagen. Die Verwendung grepwar eine schnelle und einfache Lösung nach einigen Kopfschmerzen.

Lemminge19
quelle
3

Keine der vorherigen Antworten ist unter Ubuntu gut. Versuche dies:

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

Ich habe das hier gefunden

Sixro
quelle
Ich sehe keinen Grund, warum eine der Antworten mit mehr als 100 Punkten unter Ubuntu nicht funktionieren sollte.
Axel Beckert
mmm mal sehen Vielleicht, weil ich sie alle ausprobiert habe?
Sixro
find ist überall die gleiche Implementierung auf allen Linux-Distributionen - die aus dem GNU-Projekt. Der einzige Unterschied könnten die Versionen sein. Aber die Änderungen im letzten Jahrzehnt waren nicht so invasiv, außer vielleicht für den Berechtigungsabgleich.
Axel Beckert
3

Dies ist für mich auf einem Mac geeignet:

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

Es wird ausschließen vendorund app/cachedir für die Suche Namen, der mit dem Suffix php.

jiahut
quelle
Setzen Sie lieber einfache Anführungszeichen um '* .php', sonst finden Sie nicht das, wonach Sie suchen.
Br.Bill
3

Für diejenigen unter Ihnen unter älteren UNIX-Versionen, die -path oder -not nicht verwenden können

Getestet mit SunOS 5.10 Bash 3.2 und SunOS 5.11 Bash 4.4

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f
JaredTS486
quelle
Könnte mehr als das angegebene Verzeichnis übergeben.
MUY Belgien
2

How-to-use-Prune-Option-of-Fund-in-sh ist eine ausgezeichnete Antwort von Laurence Gonsalves , wie -prunefunktioniert.

Und hier ist die generische Lösung:

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

Um zu vermeiden, dass Sie /path/to/seach/mehrmals tippen, wickeln Sie das findin ein pushd .. popdPaar.

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
quelle
1
Unter stackoverflow.com/questions/4210042/… habe ich erfahren, dass die für das -pathMuss verwendete Syntax mit dem Namen übereinstimmen muss, der gefunden wird, wenn das Verzeichnis beispielsweise so gedruckt wird find . -path ./.git -prune -o -print, oder find $HOME/foo -path $HOME/foo/.git -prune -o -print einige der Antworten sagen nur, -path somedirwas leider der Fall ist nicht genau genug, um nützlich zu sein.
PatS
2

Für das, was ich brauchte, funktionierte es so, landscape.jpgauf allen Servern beginnend mit root zu finden und die Suche im /varVerzeichnis auszuschließen :

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

find / -maxdepth 1 -type dlistet alle d irectories in/

grep -v /var schließt `/ var 'von der Liste aus

xargs -I '{}' find '{}' -name landscape.jpgFühren Sie einen beliebigen Befehl aus, wie findbei jedem Verzeichnis / Ergebnis aus der Liste

adrianTNT
quelle
Warten Sie eine Sekunde, /ist noch nicht ausgeschlossen. Möglicherweise benötigen Sie sed 1d.
Simba
2

Die folgenden Befehle funktionieren:

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

Wenn Sie ein Problem mit der Suche haben, verwenden Sie die -D treeOption, um die Informationen zur Ausdrucksanalyse anzuzeigen.

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

Oder die -D all, um alle Ausführungsinformationen anzuzeigen.

find -D all . -path ./.git -prune -o -print
EIIPII
quelle
1

Ich fand den Funktionsnamen in C-Quellendateien ausschließen * .o und ausschließen * .swp und ausschließen (keine reguläre Datei) und ausschließen dir Ausgabe mit diesem Befehl:

find .  \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach
Iwan Iwanowitsch
quelle
1

Verwenden Sie die execAktion besser als die forSchleife:

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

Das exec ... '{}' ... '{}' \;wird einmal für jede übereinstimmende Datei ausgeführt, wobei die geschweiften Klammern '{}'durch den aktuellen Dateinamen ersetzt werden.

Beachten Sie, dass die geschweiften Klammern in einfache Anführungszeichen gesetzt sind, um sie vor der Interpretation als Shell-Skript-Interpunktion * zu schützen .


Anmerkungen

* Aus dem Abschnitt BEISPIELE der find (GNU findutils) 4.4.2Manpage

Alberto
quelle
1
Sehr alte Frage, aber noch Raum für Verbesserungen. Ich fand es zufällig, ein ähnliches Problem zu lösen, und keine der Antworten war zufriedenstellend.
Alberto
Ich benutze die execAktion oft und finde sie sehr nützlich. Normalerweise füge ich Anführungszeichen hinzu, {}falls in den Dateipfaden Leerzeichen vorhanden sind "{}".
Ludovic Kuty
@lkuty Ich wollte gerade meinen Beitrag bearbeiten, um Ihren Kommentar wiederzugeben, aber nach einem kurzen Test (ohne Anführungszeichen, {}funktioniert für Dateien mit Leerzeichen im Namen) und einem Blick in die Manpages scheint das Anführen nur zu vermeiden Sie werden als Interpunktion für Shell-Skripte falsch interpretiert. In diesem Fall würden Sie einfache Anführungszeichen verwenden:'{}'
Alberto
Ich denke, dass ich es verwenden musste, um cpoder mvoder zu machen rm. Ich werde es überprüfen
Ludovic Kuty
1

Ich habe den obigen Befehl ausprobiert, aber keiner von denen, die "-prune" verwenden, funktioniert für mich. Schließlich habe ich dies mit dem folgenden Befehl ausprobiert:

find . \( -name "*" \) -prune -a ! -name "directory"
Jolly Liu
quelle