Melewati banyak direktori ke opsi -prune di find

9

Saya menggunakan finduntuk mencari dan menghapus file cadangan tetapi ingin mengecualikan direktori tertentu dari pencarian. Nama file backup bisa berhenti dalam .bck, bak, ~, atau backup.

Kode Minimal Working Example (MWE) dengan hanya tiga direktori untuk dikecualikan adalah:

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

Sintaksnya \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunetampak agak kikuk, terutama jika ada sekitar sepuluh direktori yang harus dikecualikan, meskipun saya telah menunjukkan hanya tiga di MWE.

Apakah ada cara yang lebih elegan menggunakan file input, dengan daftar direktori yang dikecualikan, atau susunan array atau daftar, yang dapat ditekan ke dalam layanan?

Saya minta maaf karena tidak lebih eksplisit ketika saya menulis pertanyaan asli saya.

NB: trash-putadalah utilitas yang memindahkan file ke Trashcanbukan menghapusnya [1].

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

chandra
sumber

Jawaban:

4

Sejauh yang saya tahu, tidak ada opsi untuk memberitahu finduntuk membaca pola dari file. Solusi yang mudah adalah menyimpan pola yang ingin saya kecualikan dalam file dan berikan file itu sebagai input untuk kebalikannya grep. Sebagai contoh, saya telah membuat file dan direktori berikut:

$ 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

Jika saya mengerti contoh Anda diposting dengan benar, Anda ingin pindah a.bck, .aa.bak, b.bak, c~, foo.backupdan dir2/bb2.bakuntuk sampah dan cuti .aa.bak, .dir1/bb1.bak, Documents/Documents.bakdan Music/Music.bakdi mana mereka berada. Karena itu, saya telah membuat file exclude.txtdengan konten berikut (Anda dapat menambahkan sebanyak yang Anda inginkan):

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

Saya menggunakan ./.*/karena saya mengerti temuan asli Anda berarti Anda ingin memindahkan file cadangan tersembunyi ( .foo) yang ada di direktori saat ini tetapi mengecualikan file cadangan apa pun yang ada di direktori tersembunyi ( .foo/bar). Jadi, sekarang saya dapat menjalankan findperintah dan gunakan grepuntuk mengecualikan file yang tidak diinginkan:

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

Opsi 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
sumber
Saya minta maaf karena tidak eksplisit. Mohon lihat pertanyaan revisi yang saya harap lebih jelas.
chandra
@chandra lihat jawaban yang diperbarui, ide umum yang sama, detail berbeda.
terdon
Terima kasih. Anda telah menjawab pertanyaan saya dengan sangat jelas dan sempurna untuk tujuan saya. Saya telah menerima jawaban Anda.
chandra
6

Dengan GNU find (yaitu di bawah Linux atau Cygwin yang tidak tertanam), Anda dapat menggunakan -regexuntuk menggabungkan semua -pathwildcard ini menjadi satu regex tunggal.

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

Dengan FreeBSD atau OSX, gunakan -Esebagai ganti -regextype posix-extended.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih atas jawaban alternatif yang bagus. Sayang sekali saya tidak bisa menerima dua jawaban.
chandra
2

Kelompokkan -path ... -pruneke dalam satu ekspresi yang diapit dengan \( ... \)menggunakan -o( atau ) logika.

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

Contoh akan direktori tidak iterate atau file pada atau di bawah /somepath/a, /somepath/bdan /somepath/c.

Berikut adalah contoh yang lebih spesifik menggunakan beberapa tindakan.

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

Ini tampaknya lebih merupakan pertanyaan shell daripada findpertanyaan. Dengan file yang mengandung ( -name dir1 -o -name dir2 ) -prune(no "\"!) Anda cukup melakukan ini:

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

Tanpa mengubah panggilan penemuan itu sendiri (ke eval findatau dengan mengubah $ IFS) ini berfungsi dengan jalur tanpa spasi putih saja.

Jika Anda ingin membuat file lebih sederhana, Anda dapat menulis skrip.

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

Dan gunakan

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

sebagai gantinya.

Hauke ​​Laging
sumber
Saya minta maaf karena tidak eksplisit. Mohon lihat pertanyaan revisi yang saya harap lebih jelas.
chandra
@ Chandra Saya tidak melihat bagaimana pertanyaan Anda lebih jelas atau saya tidak mengerti apa yang bisa menjadi masalah dengan solusi saya (kecuali untuk penggantian sepele -nameoleh path).
Hauke ​​Laging
Script saya di atas berfungsi dan melakukan apa yang saya inginkan. Saya hanya ingin tahu apakah ada cara yang lebih rapi daripada \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunemengecualikan direktori tertentu dari pencarian rekursif find. Saya tidak mencari apa pun di dalam file melainkan menghapus file tertentu dan menghindari direktori tertentu di jalur pencarian saya. Saya juga tidak mengerti apa yang sedang dilakukan skrip Anda. Jadi, sepertinya kita memiliki miskomunikasi. Maaf. Mari kita berhenti di situ.
chandra