ผ่านหลายไดเรกทอรีไปยังตัวเลือก -prune ในการค้นหา


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ให้อ่านรูปแบบจากไฟล์ วิธีแก้ปัญหาที่ง่ายคือการบันทึกรูปแบบที่ฉันต้องการที่จะไม่รวมอยู่ในแฟ้มและส่งผ่านไฟล์ที่เป็น input 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 (เช่นภายใต้ non-embedded Linux หรือ Cygwin) คุณสามารถใช้-regexเพื่อรวม-pathสัญลักษณ์ทั้งหมดเหล่านี้เป็น 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

ด้วย 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 call เอง (ไปยัง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

สคริปต์ของฉันด้านบนใช้งานได้และทำตามที่ฉันต้องการ ฉันก็อยากจะรู้ว่ามีวิธี neater กว่า\( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneยกเว้นไดเรกทอรีบางอย่างจากการค้นหา recursive ที่findไม่ ฉันไม่ได้ค้นหาอะไรเลยในไฟล์ แต่ต้องการลบไฟล์บางไฟล์และหลีกเลี่ยงไดเรกทอรีในพา ธ การค้นหาของฉัน ฉันไม่เข้าใจว่าสคริปต์ของคุณพยายามทำอะไร ดังนั้นดูเหมือนว่าเรามีการสื่อสารผิดพลาด ขอโทษ ขอให้เราทิ้งไว้ที่นั่น
จันทรา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.