디렉토리를 반복적으로 반복하여 특정 확장자를 가진 파일을 삭제하는 방법


157

디렉토리를 반복적으로 반복하고 확장자가 .pdf및 인 모든 파일을 제거해야합니다 .doc. 디렉토리를 반복적으로 반복하지만 위에서 언급 한 파일 확장자로 파일을 필터링하지는 않습니다.

지금까지 내 코드

#/bin/sh

SEARCH_FOLDER="/tmp/*"

for f in $SEARCH_FOLDER
do
    if [ -d "$f" ]
    then
        for ff in $f/*
        do      
            echo "Processing $ff"
        done
    else
        echo "Processing file $f"
    fi
done

아무데도 못 가니까 코드를 완성하는 데 도움이 필요합니다.


68
코드를 이해하지 않고 코드를 실행하는 것은 좋지 않은 방법이라는 것을 알고 있지만 많은 사람들이이 사이트를 방문하여 bash 스크립팅을 배우고 있습니다. 나는 "bash scripting files recursively"인터넷 검색을 통해 여기에 왔으며 파일 을 삭제한다는 사실을 깨닫지 않고 거의 (재귀를 테스트하기 위해) 이러한 답변 중 하나를 실행했습니다. rmOP 코드의 일부이지만 실제로 묻는 질문과 관련이 없다는 것을 알고 있습니다. 과 같은 무해한 명령을 사용하여 답변을 구하면 더 안전하다고 생각합니다 echo.
Keith

비슷한 질문이 여기에 있습니다 : stackoverflow.com/questions/41799938/…
codeforester

1
@Keith는 비슷한 경험을 가지고 있으며 완전히 동의하고 제목을 변경했습니다.
idclev 463035818

답변:


146

find 그것을 위해 만들어졌습니다.

find /tmp -name '*.pdf' -or -name '*.doc' | xargs rm

19
또는 찾기 -delete옵션.
Matthew Flaschen

28
find ... -print0 | xargs -0 ...원시 찾기가 아닌 항상 사용해야합니다 . | 줄 바꿈이 포함 된 파일 이름 문제를 피하기 위해 xargs.
Grumbel

7
xargs옵션없이 사용 하는 것은 거의 항상 나쁜 조언이며 예외는 아닙니다. find … -exec대신 사용하십시오 .
Gilles 'SO- 악 그만

211

mouviciel의 답변에 대한 후속 조치로 xargs를 사용하는 대신 for 루프 로이 작업을 수행 할 수도 있습니다. xargs는 종종 번거 롭습니다. 특히 각 반복에서 더 복잡한 것을 수행 해야하는 경우에 특히 그렇습니다.

for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm $f; done

많은 사람들이 언급했듯이 파일 이름에 공백이 있으면 실패합니다. IFS (내부 필드 구분 기호)를 줄 바꿈 문자로 임시 설정하여이 문제를 해결할 수 있습니다. \[?*파일 이름에 와일드 카드 문자가있는 경우에도 실패 합니다. 와일드 카드 확장 (글 로빙)을 일시적으로 비활성화하여이 문제를 해결할 수 있습니다.

IFS=$'\n'; set -f
for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm "$f"; done
unset IFS; set +f

파일 이름에 줄 바꿈이 있으면 작동하지 않습니다. xargs 기반 솔루션을 사용하는 것이 좋습니다.

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -print0 | xargs -0 rm

(여기에서 이스케이프 처리 된 괄호는 -print0or조항 모두에 적용됩니다 .)

GNU와 * BSD find에도 -delete다음과 같은 동작이 있습니다.

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -delete

27
파일 이름에 공백이 있으면 for 루프가 공백에서 찾기 결과를 분할합니다.
trev

3
공백에서 어떻게 쪼개지나요? 비슷한 것을 시도하고 있으며이 루프를 망칠 공백이있는 많은 디렉토리가 있습니다.
Christian

3
매우 유용한 답변이기 때문에?
zenperttu

1
@Christian "$ (find ...)"와 같은 인용 부호를 사용하여 공백 분할을 수정하십시오. 제임스의 답변을 편집하여 표시했습니다.
Matthew

2
@Matthew 편집은 전혀 수정하지 않았습니다. 실제로 찾은 고유 파일이있는 경우에만 명령이 작동하도록했습니다 . 파일 이름에 공백, 탭 등이 없으면 이 버전이 작동 합니다. 이전 버전으로 롤백했습니다. 현명한 것을 지적하면 실제로 고칠 수 있습니다 for f in $(find ...). 이 방법을 사용하지 마십시오.
gniourf_gniourf

67

없이 find:

for f in /tmp/* tmp/**/* ; do
  ...
done;

/tmp/*디렉토리에 /tmp/**/*있는 파일이며 하위 폴더에있는 파일입니다. globstar 옵션 ( shopt -s globstar) 을 활성화해야 할 수도 있습니다 . 따라서 질문의 코드는 다음과 같아야합니다.

shopt -s globstar
for f in /tmp/*.pdf /tmp/*.doc tmp/**/*.pdf tmp/**/*.doc ; do
  rm "$f"
done

이를 위해서는 bash ≥4.0 (또는 zsh없이 shopt -s globstar또는 set -o globstar대신 ksh) 이 필요합니다 shopt -s globstar. 또한 bash <4.3에서는 디렉토리뿐만 아니라 디렉토리에 대한 심볼릭 링크를 통과하므로 일반적으로 바람직하지 않습니다.


1
이 방법은 OSX에 공백이 포함 된 파일 이름으로도
효과적이었습니다.

2
globstar는 Bash 4.0 이상에서만 사용 가능하다는 점에 주목할 필요가 있습니다. 이는 많은 컴퓨터에서 기본 버전이 아닙니다.
트로이 하워드

1
첫 번째 인수를 지정해야한다고 생각하지 않습니다. (적어도 현재로서는) for f in /tmp/**충분합니다. / tmp dir의 파일을 포함합니다.
phil294

1
이게 더 나을까요? for f in /tmp/*.{pdf,doc} tmp/**/*.{,pdf,doc} ; do
Ice-Blaze

1
**좋은 확장이지만 POSIX로 이식 할 수는 없습니다 sh. (이 질문에는 bash 태그가 붙어 있지만 여기의 여러 솔루션과 달리 이것은 실제로 Bash 전용입니다. 또는 다른 여러 확장 쉘에서도 작동합니다.)
tripleee

27

재귀 적으로 무언가를 원한다면 재귀를 사용하는 것이 좋습니다 (예, 스택 등을 사용하여 수행 할 수는 있지만 헤이).

recursiverm() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursiverm)
    fi
    rm -f *.pdf
    rm -f *.doc
  done
}

(cd /tmp; recursiverm)

즉, find이미 제안 된 것처럼 더 나은 선택 일 것입니다.


15

다음은 쉘 ( bash)을 사용하는 예입니다 .

#!/bin/bash

# loop & print a folder recusively,
print_folder_recurse() {
    for i in "$1"/*;do
        if [ -d "$i" ];then
            echo "dir: $i"
            print_folder_recurse "$i"
        elif [ -f "$i" ]; then
            echo "file: $i"
        fi
    done
}


# try get path from param
path=""
if [ -d "$1" ]; then
    path=$1;
else
    path="/tmp"
fi

echo "base path: $path"
print_folder_recurse $path

15

이것은 귀하의 질문에 직접 대답하지는 않지만 원 라이너로 문제를 해결할 수 있습니다.

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -exec rm {} +

find의 일부 버전 (GNU, BSD)에는 다음 -delete을 호출하는 대신 사용할 수 있는 작업이 있습니다 rm.

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -delete

7

이 방법은 공백을 잘 처리합니다.

files="$(find -L "$dir" -type f)"
echo "Count: $(echo -n "$files" | wc -l)"
echo "$files" | while read file; do
  echo "$file"
done

개별 수정, 수정

function count() {
    files="$(find -L "$1" -type f)";
    if [[ "$files" == "" ]]; then
        echo "No files";
        return 0;
    fi
    file_count=$(echo "$files" | wc -l)
    echo "Count: $file_count"
    echo "$files" | while read file; do
        echo "$file"
    done
}

에코 후 "-n"플래그가 필요하지 않다고 생각합니다. "-n"을 사용하면 스크립트가 잘못된 수의 파일을 제공합니다. 디렉토리에서 정확히 하나의 파일에 대해 "Count : 0"을 출력합니다.
Lopa

1
모든 파일 이름에서 작동하지는 않습니다. 이름 끝에 공백이 있고 파일 이름에는 줄 바꿈이 있고 일부 파일 이름에는 백 슬래시가 있습니다. 이러한 결함은 수정 될 수 있지만 전체 접근 방식은 불필요하게 복잡하므로 귀찮게 할 가치가 없습니다.
Gilles 'SO- 악한 중지'

3

bash (버전 4.0부터) :

shopt -s globstar nullglob dotglob
echo **/*".ext"

그게 다야.
해당 확장자를 가진 파일 (또는 디렉토리)을 선택하기위한 후행 확장자 ".ext"

옵션 globstar는 ** (재귀 적으로 검색)를 활성화합니다.
nullglob 옵션은 파일 / 디렉토리와 일치하지 않는 경우 *를 제거합니다.
옵션 dotglob에는 점으로 시작하는 파일 (숨겨진 파일)이 포함되어 있습니다.

bash 4.3 이전 **/에는 바람직하지 않은 디렉토리에 대한 심볼릭 링크를 탐색합니다.


1

다음 함수는 디렉토리의 모든 \home\ubuntu디렉토리 (ubuntu 아래의 전체 디렉토리 구조)를 반복적으로 반복 하고 필요한 검사를 else블록 단위로 적용합니다 .

function check {
        for file in $1/*      
        do
        if [ -d "$file" ]
        then
                check $file                          
        else
               ##check for the file
               if [ $(head -c 4 "$file") = "%PDF" ]; then
                         rm -r $file
               fi
        fi
        done     
}
domain=/home/ubuntu
check $domain

1

이것이 내가하는 가장 간단한 방법입니다. rm **/@(*.doc|*.pdf)

** 이 작업을 재귀 적으로 만듭니다

@(*.doc|*.pdf) pdf 또는 doc로 끝나는 파일을 찾습니다.

쉽고 안전하게 교체하여 테스트 rmls


0

출력 find을 다른 유틸리티로 파이프 할 이유가 없습니다 . find-delete그것으로 내장 플래그.

find /tmp -name '*.pdf' -or -name '*.doc' -delete

0

제공된 다른 답변에는로 시작하는 파일이나 디렉토리는 포함되지 않습니다. 다음은 나를 위해 일했습니다.

#/bin/sh
getAll()
{
  local fl1="$1"/*;
  local fl2="$1"/.[!.]*; 
  local fl3="$1"/..?*;
  for inpath in "$1"/* "$1"/.[!.]* "$1"/..?*; do
    if [ "$inpath" != "$fl1" -a "$inpath" != "$fl2" -a "$inpath" != "$fl3" ]; then 
      stat --printf="%F\0%n\0\n" -- "$inpath";
      if [ -d "$inpath" ]; then
        getAll "$inpath"
      #elif [ -f $inpath ]; then
      fi;
    fi;
  done;
}

-1

그냥 해

find . -name '*.pdf'|xargs rm

4
아니, 이러지 마 공백이나 기타 재미있는 기호가 포함 된 파일 이름이 있으면 중단됩니다.
gniourf_gniourf

-1

다음은 주어진 디렉토리를 반복적으로 반복하고 모든 내용을 나열합니다.

for d in /home/ubuntu/*; do echo "listing contents of dir: $d"; ls -l $d/; done


아니요,이 함수는 재귀 적으로 어떤 것도 통과하지 않습니다. 하위 디렉토리의 내용 만 나열합니다. 그냥 보풀이 ls -l /home/ubuntu/*/많기 때문에 쓸모가 없습니다.
Gilles 'SO- 악한 중지'

-1

명령 실행에 사용 된 쉘을 변경할 수 있으면 ZSH를 사용하여 작업을 수행 할 수 있습니다.

#!/usr/bin/zsh

for file in /tmp/**/*
do
    echo $file
done

모든 파일 / 폴더를 반복적으로 반복합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.