Przekazywanie wielu katalogów do opcji -prune w find

9

Używam finddo lokalizowania i usuwania plików kopii zapasowych, ale chcę wykluczyć niektóre katalogi z wyszukiwania. Nazwy plików kopii zapasowych mógł zakończyć w .bck, bak, ~, lub backup.

Kod minimalnego przykładu roboczego (MWE) z tylko trzema katalogami do wykluczenia to:

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

Składnia \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunewydaje się trochę niezgrabna, szczególnie jeśli jest około dziesięciu katalogów do wykluczenia, chociaż pokazałem tylko trzy w MWE.

Czy istnieje bardziej elegancki sposób korzystania z pliku wejściowego z listą wykluczonych katalogów lub z konstrukcją podobną do tablicy lub listy, którą można wprowadzić do usługi?

Przykro mi, że nie wyraziłem się jasno, kiedy napisałem swoje oryginalne pytanie.

NB: trash-putto narzędzie, które przenosi pliki Trashcanzamiast je usuwać [1].

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

chandra
źródło

Odpowiedzi:

4

O ile mi wiadomo, nie ma opcji, findaby odczytać wzorce z pliku. Łatwym obejściem jest zapisanie w pliku wzorców, które chcę wykluczyć, i przekazanie tego pliku jako danych wejściowych do odwrotnej kolejności grep. Na przykład utworzyłem następujące pliki i katalogi:

$ 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

Gdybym rozumiał przykład masz wysłana poprawnie, chcesz przenieść a.bck, .aa.bak, b.bak, c~, foo.backupi dir2/bb2.bakdo kosza i urlopu .aa.bak, .dir1/bb1.bak, Documents/Documents.baki Music/Music.bakgdzie są. Dlatego utworzyłem plik exclude.txtz następującą zawartością (możesz dodać tyle, ile chcesz):

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

Używam, ./.*/ponieważ zrozumiałem, że twoje oryginalne znalezisko oznacza, że ​​chcesz przenieść ukryte pliki kopii zapasowych ( .foo), które znajdują się w bieżącym katalogu, ale wykluczam wszelkie pliki kopii zapasowych, które znajdują się w ukrytych katalogach ( .foo/bar). Mogę teraz uruchomić findpolecenie i użyć grepdo wykluczenia niechcianych plików:

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

Opcje grep:

   -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
źródło
Bardzo mi przykro, że nie wyraziłem się jasno. Łaskawie zobacz poprawione pytanie, które, mam nadzieję, jest bardziej zrozumiałe.
chandra
@chandra zobacz zaktualizowaną odpowiedź, ten sam ogólny pomysł, różne szczegóły.
terdon
Dziękuję Ci. Odpowiedziałeś na moje pytanie bardzo jasno i doskonale dla mojego celu. Zaakceptowałem twoją odpowiedź.
chandra
6

Dzięki GNU find (tj. Pod niewbudowanym Linuksem lub Cygwinem) możesz użyć, -regexaby połączyć wszystkie te -pathsymbole wieloznaczne w jeden regex.

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

W FreeBSD lub OSX użyj -Ezamiast -regextype posix-extended.

Gilles „SO- przestań być zły”
źródło
Dziękuję za doskonałą alternatywną odpowiedź. Szkoda, że ​​nie mogę zaakceptować dwóch odpowiedzi.
chandra
3

Grupuj -path ... -prunew jedno wyrażenie zamknięte za \( ... \)pomocą logiki -o( lub ).

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

Przykład nie będzie iteracyjne katalogów lub plików na lub poniżej /somepath/a, /somepath/boraz /somepath/c.

Oto bardziej konkretny przykład z użyciem wielu akcji.

find / \( -path /dev -prune -o \
          -path /proc -prune -o \
          -path /sys -prune \
       \) \
       -o -printf '%p ' -exec cksum {} \;
JamesThomasMoon1979
źródło
1

Wydaje się, że jest to raczej pytanie podstawowe niż findpytanie. Z plikiem zawierającym ( -name dir1 -o -name dir2 ) -prune(bez „\”!) Możesz po prostu to zrobić:

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

Bez zmiany samego wywołania find (do eval findlub przez zmianę $ IFS) działa to jednak tylko ze ścieżkami bez białych znaków.

Jeśli chcesz uprościć plik, możesz napisać skrypt.

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

I użyć

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

zamiast.

Hauke ​​Laging
źródło
Bardzo mi przykro, że nie wyraziłem się jasno. Łaskawie zobacz poprawione pytanie, które, mam nadzieję, jest bardziej zrozumiałe.
chandra
@chandra ja ani zobaczyć, jak Twoje pytanie jest jaśniejszy, ani nie rozumiem, co może być problem z moim rozwiązanie (z wyjątkiem trywialnego replecement dnia -nameprzez path).
Hauke ​​Laging
Mój skrypt powyżej działa i robi to, co chcę. Chciałem po prostu wiedzieć, czy istnieje lepszy sposób niż \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunewykluczenie niektórych katalogów z wyszukiwania rekurencyjnego find. Nie szukam niczego w plikach, ale raczej usuwam określone pliki i unikam pewnych katalogów na ścieżce wyszukiwania. Nie rozumiem też, co twój skrypt próbuje zrobić. Wygląda na to, że mamy nieporozumienia. Przepraszam. Zostawmy to w tym miejscu.
chandra