Übergeben mehrerer Verzeichnisse an die Option -prune in find

9

Ich verwende findzum Suchen und Löschen von Sicherungsdateien, möchte jedoch bestimmte Verzeichnisse von der Suche ausschließen. Die Backup - Dateinamen enden in könnten .bck, bak, ~, oder backup.

Der MWE-Code (Minimal Working Example) mit nur drei auszuschließenden Verzeichnissen lautet:

#! /bin/bash
find . -type d \( -path "./.*" -o -path "./Music" -o -path "./Documents" \) -prune -o -type f \( -name "*.bck" -o -name "*.bak" -o -name "*~" -o -name "*.backup" \) -print0 | xargs -0 --no-run-if-empty trash-put

Die Syntax \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunescheint etwas klobig zu sein, besonders wenn etwa zehn Verzeichnisse ausgeschlossen werden müssen, obwohl ich in der MWE nur drei gezeigt habe.

Gibt es eine elegantere Möglichkeit, entweder eine Eingabedatei mit der Liste der ausgeschlossenen Verzeichnisse oder ein Array- oder Listen-ähnliches Konstrukt zu verwenden, das in Betrieb genommen werden könnte?

Es tut mir leid, dass ich nicht expliziter war, als ich meine ursprüngliche Frage schrieb.

NB: trash-putist ein Dienstprogramm, mit dem die Dateien nach Trashcanverschoben werden, anstatt sie zu löschen [1].

[1]. https://github.com/andreafrancia/trash-cli

Chandra
quelle

Antworten:

4

Soweit ich weiß, gibt es keine Möglichkeit, findMuster aus einer Datei zu lesen. Eine einfache Problemumgehung besteht darin, die Muster, die ich ausschließen möchte, in einer Datei zu speichern und diese Datei als Eingabe für eine Umkehrung zu übergeben grep. Als Beispiel habe ich die folgenden Dateien und Verzeichnisse erstellt:

$ tree -a
.
├── a
├── .aa
├── .aa.bak
├── a.bck
├── b
├── .dir1
│   └── bb1.bak
├── dir2
│   └── bb2.bak
├── b.bak
├── c
├── c~
├── Documents
│   └── Documents.bak
├── exclude.txt
├── foo.backup
└── Music
    └── Music.bak

Wenn ich das Beispiel Sie richtig verstanden geschrieben, die Sie verschieben wollen a.bck, .aa.bak, b.bak, c~, foo.backupund dir2/bb2.bakin den Papierkorb und verlassen .aa.bak, .dir1/bb1.bak, Documents/Documents.bakund Music/Music.bakwo sie sind. Ich habe daher die Datei exclude.txtmit folgendem Inhalt erstellt (Sie können beliebig viele hinzufügen):

$ cat exclude.txt 
./.*/
./Music
./Documents

Ich verwende, ./.*/weil ich Ihren ursprünglichen Fund so verstanden habe, dass Sie versteckte Sicherungsdateien ( .foo) im aktuellen Verzeichnis verschieben möchten, aber alle Sicherungsdateien ausschließen möchten, die sich in versteckten Verzeichnissen befinden ( .foo/bar). So kann ich jetzt den findBefehl ausführen und grepdamit unerwünschte Dateien ausschließen:

$ find . -type f | grep -vZf exclude.txt | xargs -0 --no-run-if-empty trash-put

Grep-Optionen:

   -v, --invert-match
          Invert  the  sense  of matching, to select non-matching
          lines.  (-v is specified by POSIX.)
   -f FILE, --file=FILE
          Obtain patterns from FILE, one  per  line.   The  empty
          file  contains  zero  patterns,  and  therefore matches
          nothing.  (-f is specified by POSIX.)
   -Z, --null
          Output a zero byte (the ASCII NUL character) instead of
          the  character  that normally follows a file name.  For
          example, grep -lZ outputs a zero byte after  each  file
          name  instead  of the usual newline.  This option makes
          the output unambiguous, even in the  presence  of  file
          names  containing  unusual  characters  like  newlines.
          This  option  can  be  used  with  commands  like  find
          -print0,  perl  -0,  sort  -z,  and xargs -0 to process
          arbitrary file names, even those that  contain  newline
          characters.
terdon
quelle
Es tut mir so leid, dass ich nicht explizit bin. Bitte sehen Sie die überarbeitete Frage, von der ich hoffe, dass sie klarer ist.
Chandra
@ Chandra siehe aktualisierte Antwort, gleiche allgemeine Idee, verschiedene Details.
Terdon
Danke. Sie haben meine Frage sehr klar und perfekt für meinen Zweck beantwortet. Ich habe deine Antwort akzeptiert.
Chandra
6

Mit GNU find (dh unter nicht eingebettetem Linux oder Cygwin) können Sie -regexalle diese -pathPlatzhalter zu einem einzigen regulären Ausdruck kombinieren .

find . -regextype posix-extended \
     -type d -regex '\./(\..*|Music|Documents)' -prune -o \
     -type f -regex '.*(\.(bck|bak|backup)|~)' -print0 |
xargs -0 --no-run-if-empty trash-put

Verwenden Sie bei FreeBSD oder OSX -Eanstelle von -regextype posix-extended.

Gilles 'SO - hör auf böse zu sein'
quelle
Vielen Dank für eine hervorragende alternative Antwort. Es ist eine Schande, dass ich zwei Antworten nicht akzeptieren kann.
Chandra
2

Gruppieren Sie -path ... -prunein einem Ausdruck, der \( ... \)mithilfe von -o( oder ) Logik eingeschlossen ist.

find /somepath \( -path /a -prune -o \
                  -path /b -prune -o \
                  -path /c -prune \
               \) \
               -o -print

Das Beispiel wird nicht Iterierte Verzeichnisse oder Dateien auf oder unter /somepath/a, /somepath/bund /somepath/c.

Hier ist ein spezifischeres Beispiel mit mehreren Aktionen.

find / \( -path /dev -prune -o \
          -path /proc -prune -o \
          -path /sys -prune \
       \) \
       -o -printf '%p ' -exec cksum {} \;
JamesThomasMoon1979
quelle
1

Dies scheint eher eine Shell-Frage als eine findFrage zu sein. Mit einer Datei, die ( -name dir1 -o -name dir2 ) -prune(kein "\"!) Enthält, können Sie dies einfach tun:

find ... $(< /path/to/file)

Ohne den Suchaufruf selbst zu eval findändern (auf oder durch Ändern von $ IFS) funktioniert dies jedoch nur mit Pfaden ohne Leerzeichen.

Wenn Sie die Datei einfacher halten möchten, können Sie ein Skript schreiben.

# file content
dir1
dir2
dir3

# script content
#!/bin/bash
file=/path/to/file
# file may be checked for whitespace here
grep '[^[:space:]]' "$file" | { empty=yes
  while read dir; do
    if [ yes = "$empty" ]; then
      echo -n "( "
      empty=no
    else
      echo -n " -o "
    fi
    echo -n "-name ${dir}"
  done
  if [ no = "$empty" ]; then
    echo -n " ) -prune"
  fi; }

Und verwenden

find ... $(/path/to/script)

stattdessen.

Hauke ​​Laging
quelle
Es tut mir so leid, dass ich nicht explizit bin. Bitte sehen Sie die überarbeitete Frage, von der ich hoffe, dass sie klarer ist.
Chandra
@chandra Ich sehe weder, wie Ihre Frage klarer ist, noch verstehe ich, was ein Problem mit meiner Lösung sein könnte (mit Ausnahme der trivialen Wiederholung von -nameby path).
Hauke ​​Laging
Mein Skript oben funktioniert und macht, was ich will. Ich wollte einfach nur wissen, ob es einen besseren Weg gibt, als \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunebestimmte Verzeichnisse von der rekursiven Suche auszuschließen find. Ich suche nicht nach etwas in Dateien, sondern lösche bestimmte Dateien und vermeide bestimmte Verzeichnisse in meinem Suchpfad. Ich verstehe auch nicht, was Ihr Skript versucht. Es scheint also, dass wir eine Fehlkommunikation haben. Es tut uns leid. Lassen wir es dabei.
Chandra