find의 -prune 옵션에 여러 디렉토리 전달

find의 -prune 옵션에 여러 디렉토리 전달

백업 파일을 찾고 삭제하는 데 사용하고 있는데 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" \) -pruneMWE에서는 3개만 표시했지만 제외할 디렉터리가 10개 정도 있는 경우 구문이 약간 투박해 보입니다.

서비스에 푸시할 수 있는 입력 파일(제외된 디렉터리 목록 포함)이나 배열 또는 목록과 같은 구성을 사용하는 더 우아한 방법이 있습니까?

원래 질문을 작성할 때 더 명확하지 않아서 죄송합니다.

참고: trash-put파일을 삭제하는 대신 파일을 이동하는 Trashcan유틸리티 입니다[1].

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

답변1

사용 -path ... -prune(-o또는) 괄호로 그룹화된 논리\( ... \)

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

이 예제는 /somepath/a디렉터리나 파일을 반복하지 않습니다./somepath/b/somepath/c


이것은 여러 표현식과 복잡한 연산을 사용하여 고안된 예입니다 -exec. 이는 Linux 호스트의 순수 파일에 대한 파일 경로와 체크섬을 인쇄하고 주로 임시 파일이나 문자 장치가 포함된 경로를 잘라냅니다.

$ find / \( -path /dev -prune -o \
            -path /proc -prune -o \
            -path /sys -prune \
         \) \
         -o -type f \
            -printf '%p ' -exec sh -c 'md5sum -- "{}" | cut -f1 -d" "' \;
/etc/services 00060e37207f950bf0ebfd25810c19b9
/etc/lsb-release f317530ede1f20079f73063065c1684e
/etc/protocols bb9c019d6524e913fd72441d58b68216
/etc/rsyslog.conf 8f03326e3d7284ef50ac6777ef8a4fb8
...

답변2

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

BSD 또는 macOS -E에서는 -regextype posix-extended.

또한 이를 더 짧은 표준어로 대체할 수도 있습니다 -print0 | xargs -0 --no-run-if-empty trash-put.--no-run-if-empty-exec trash-put {} +


1 그러나 더 짧은 -r형식은 일부 다른 구현에서 지원되며 일부 BSD에서는 기본 구현이기도 합니다.

답변3

find내가 아는 한, 파일에서 패턴을 읽으라고 지시하는 옵션은 없습니다 . 간단한 해결 방법은 제외하려는 패턴을 파일에 저장하고 해당 파일을 reverse 의 입력으로 전달하는 것입니다 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.bakb.bakc~foo.backupdir2/bb2.bak.aa.bak.dir1/bb1.bakDocuments/Documents.bakMusic/Music.bakexclude.txt

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

귀하의 초기 결과가 현재 디렉터리( )의 숨겨진 백업 파일을 이동하고 숨겨진 디렉터리( )의 모든 백업 파일을 제외 ./.*/하려는 의미라는 것을 이해했기 때문에 사용합니다 . 이제 명령을 실행하고 이를 사용하여 원치 않는 파일을 제외할 수 있습니다..foo.foo/barfindgrep

$ 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.

답변4

이것은 문제라기보다는 쉘 문제처럼 보입니다 find. ("\" 없음!)을 포함하는 파일의 경우 ( -name dir1 -o -name dir2 ) -prune간단히 다음을 수행할 수 있습니다.

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

그러나 이는 찾기 호출 자체를 변경하지 않고($IFS를 변경하거나 변경하여) eval find공백이 없는 경로에만 작동합니다 .

파일을 더 간단하게 만들고 싶다면 스크립트를 작성할 수 있습니다.

# 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)

대신에.

관련 정보