Передача нескольких каталогов в опцию -prune в find

9

Я использую findдля поиска и удаления файлов резервных копий, но хочу исключить определенные каталоги из поиска. Резервные имена файлов может заканчиваться .bck, bak, ~или backup.

Код минимального рабочего примера (MWE), который нужно исключить только из трех каталогов:

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

Синтаксис \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneкажется немного неуклюжим, особенно если нужно исключить около десяти каталогов, хотя я показал только три в MWE.

Есть ли более элегантный способ использования либо входного файла со списком исключенных каталогов, либо массива или конструкции, подобной списку, которая может быть введена в действие?

Я прошу прощения за то, что не был более явным, когда я написал свой оригинальный вопрос.

NB: trash-putэто утилита, которая перемещает файлы Trashcanвместо того, чтобы удалять их [1].

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

чандра
источник

Ответы:

4

Насколько я знаю, нет возможности сказать, findчтобы прочитать шаблоны из файла. Простой обходной путь - сохранить шаблоны, которые я хочу исключить, в файл и передать этот файл в качестве входных данных для обратного grep. В качестве примера я создал следующие файлы и каталоги:

$ 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

Если я понял пример вы размещены правильно, вы хотите переместить a.bck, .aa.bak, b.bak, c~, foo.backupи dir2/bb2.bakв помойку и отпуска .aa.bak, .dir1/bb1.bak, Documents/Documents.bakи Music/Music.bakгде они находятся. Поэтому я создал файл exclude.txtсо следующим содержимым (вы можете добавить столько, сколько хотите):

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

Я использую, ./.*/потому что я понял, что ваша первоначальная находка означает, что вы хотите переместить скрытые файлы резервных копий ( .foo), которые находятся в текущем каталоге, но исключают любые файлы резервных копий, которые находятся в скрытых каталогах ( .foo/bar). Итак, теперь я могу запустить findкоманду и использовать grepдля исключения ненужных файлов:

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

Варианты 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.
Тердон
источник
Мне очень жаль, что я не был явным. Пожалуйста, смотрите пересмотренный вопрос, который, я надеюсь, более ясен.
Чандра
@chandra см обновленный ответ, та же общая идея, разные детали.
Terdon
Спасибо. Вы ответили на мой вопрос очень четко и идеально для моей цели. Я принял ваш ответ.
Чандра
6

С помощью GNU find (то есть под не встроенным Linux или Cygwin) вы можете использовать -regexдля объединения всех этих -pathподстановочных знаков в одно регулярное выражение.

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

С FreeBSD или OSX используйте -Eвместо -regextype posix-extended.

Жиль "ТАК - перестань быть злым"
источник
Спасибо за отличный альтернативный ответ. Обидно, что я не могу принять два ответа.
Чандра
2

Сгруппируйте -path ... -pruneв одно выражение, \( ... \)используя -o( или ) логику.

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

Пример не будет итерация каталоги или файлы на или под /somepath/a, /somepath/bи /somepath/c.

Вот более конкретный пример использования нескольких действий.

find / \( -path /dev -prune -o \
          -path /proc -prune -o \
          -path /sys -prune \
       \) \
       -o -printf '%p ' -exec cksum {} \;
JamesThomasMoon1979
источник
1

Похоже, это скорее вопрос оболочки, чем findвопрос. С файлом, содержащим ( -name dir1 -o -name dir2 ) -prune(без "\"!), Вы можете просто сделать это:

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

Однако, не изменяя сам вызов find ( eval findили изменяя $ IFS), это работает с путями без пробелов.

Если вы хотите сохранить файл проще, вы можете написать скрипт.

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

И использовать

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

вместо.

Хауке Лагинг
источник
Мне очень жаль, что я не был явным. Пожалуйста, смотрите пересмотренный вопрос, который, я надеюсь, более ясен.
Чандра
@chandra Я не вижу, как ваш вопрос яснее, и я не понимаю, в чем может быть проблема с моим решением (за исключением тривиального добавления -nameby path).
Хауке Лагинг
Мой скрипт выше работает и делает то, что я хочу. Я просто хотел узнать, есть ли более аккуратный способ, чем \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneисключить определенные каталоги из рекурсивного поиска, который findделает. Я не ищу ничего внутри файлов, а скорее удаляю определенные файлы и избегаю определенных каталогов в моем пути поиска. Я тоже не понимаю, что пытается сделать твой сценарий. Итак, похоже, у нас есть недопонимание. Сожалею. Давайте оставим это на этом.
Чандра