Передача декількох каталогів до опції -prune in 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.

Чи є більш елегантний спосіб використання або вхідного файлу зі списком виключених каталогів, або масиву або структури, подібної до списку, які можна було б натиснути на службу?

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

Примітка: 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

Варіанти перемикання:

   -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 (тобто під невбудованим 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.


Дякую за відмінну альтернативну відповідь. Прикро, що я не можу прийняти дві відповіді.
чандра

3

Згрупуйте -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 {} \;

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 я ні побачити , як ваше запитання ясніше і я не розумію , що може бути проблема з моїм рішенням (для тривіального replecement з крім -nameшляхом path).
Hauke ​​Laging

Мій сценарій вище працює і робить те, що я хочу. Мені просто хотілося дізнатись, чи існував більш акуратний спосіб, ніж \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneвиключати певні каталоги з рекурсивного пошуку, який findце робить. Я не шукаю нічого у файлах, а швидше видаляю певні файли та уникаю певних каталогів на своєму шляху пошуку. Я не розумію, що намагається зробити і ваш сценарій. Отож, мабуть, у нас неправильне спілкування. Вибачте. Залишимо це на цьому.
чандра
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.