여러 수준에서 지정된 파일/디렉터리를 제외한 모든 파일/디렉터리 삭제

여러 수준에서 지정된 파일/디렉터리를 제외한 모든 파일/디렉터리 삭제

한 줄에 다음을 수행하는 것이 가능합니까?

shopt -s extglob

rm -r !(folder1|file1|folder2)
rm -r folder2/!(subfolder)
rm -r folder2/subfolder/!(onefile)

예를 들면 이런 것?

rm -r !(folder1|file1|folder2/subfolder/onefile)

답변1

-path도구의 (부정적) 테스트를 사용할 수 있습니다 find. 도구가 지원하는 경우 -delete비교적 간단합니다.

find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -delete

onefile존재하는 경우 오류 가 보고 subfolder됩니다 . 이렇게 하면 생존할 수 있지만 종료 상태는 유지되지 않습니다 .folder2cannot delete: Directory not emptyfind0

find지원하는 경우 -empty디렉터리 이전에 디렉터리가 비어 있는지 테스트할 수 있습니다 -delete. 일반적으로 말하면 디렉터리가 아닌 항목이 비어 있는지 테스트하고 싶지 않습니다. 이로 인해 명령이 복잡해지지만 0모든 것이 계획대로 진행되면 명령이 반환될 수 있습니다. 이와 같이:

find . ! -path ./folder1 \
       ! -path './folder1/*' \
       ! -path ./file1 \
       ! -path ./folder2/subfolder/onefile \
       \( ! -type d -o -empty \) \
       -delete

find지원할 수 있는 자금이 충분하며 -delete아마도 -empty그렇게 될 것입니다 -regex. 내 Debian의 GNU는 find다음을 수행할 수 있습니다:

find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2/subfolder/onefile' -delete

노트:

  • onefilefind이로 인해 삭제 subfolder또는 존재가 방지됩니다 folder2. 이러한 디렉터리의 존재 여부 에 관계없이 이러한 디렉터리를 보존하려면 더 많은 패턴(아래 참조)이나 더 복잡한 정규식이 필요합니다 onefile. 더 복잡한 정규식을 사용한 변형:

    find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2\(/subfolder\(/onefile\)?\)?' -delete
    

    보너스로, 이 개선 사항은 "0이 아닌 종료 상태" 문제를 해결합니다.

  • -pathglob과 같은 패턴 일치 표기법을 사용합니다( ?단일 문자와 일치하고 *0개 이상의 문자와 일치). -regex정규식을 사용합니다( .단일 문자와 일치하고 .*0개 이상의 문자와 일치).

  • -path-regex파일의 전체 경로 와 일치합니다. 효과적인 패턴을 디자인하려면 find어떤 경로가 사용될지 알아야 합니다 . 힌트:

    • find시작점이 아닌 경로에는 후행 슬래시를 사용하지 않을 의무가 있습니다. 그래서 () 와 files -path에 대해 ./folder1별도의 모드를 사용합니다 ./folder1/*.
    • 반면에 시작점은 규정된 대로 사용됩니다. 시작점이 이면 모든 경로는 자신에 대한 경로를 제외하고 .시작됩니다 . 그러면 가 됩니다. 시작점이 이면 모든 경로가 시작됩니다../..././
  • 내 테스트에서는 -delete자동으로 처리 .하고 삭제하지 않으며 오류를 발생시키지 않으므로 제외할 필요가 없습니다 .. 반면에 오류가 발생합니다 ./. 이 동작이 얼마나 신뢰할 수 있는지 잘 모르겠습니다. 의심스러운 경우에는 명시적으로 제외하십시오 .. 특히 의미 있는 종료 상태를 찾고 있는 경우에는 더욱 그렇습니다.

  • 정규식 유형이 거의 없습니다.. 예를 들어 -regextype posix-extendedbefore를 사용할 수 있습니다 ! -regex.내가 생각 해낸 가장 간단한 명령(휴대용은 아니지만):

    find . -regextype posix-extended \
         ! -regex './folder1(/.*)?|./file1|./folder2(/subfolder(/onefile)?)?' \
           -delete
    

    .의도적으로 (리터럴 도트) 대신 (와일드카드)를 \.선두 지점으로 사용했습니다 .. 시작점이 이므로 .모든 경로는 .확실히 로 시작하므로 와일드카드는 를 제외한 어떤 것과도 일치하지 않습니다 .. \.형식적인 정확성을 선호하는 경우 사용하세요.

  • 너무 많이 삭제하지 않으려면 명령을 "테스트 실행"할 수 있습니다. -delete로 변경 -print하면 삭제하려는 항목이 표시됩니다. 이것은 다음과 같이 수정된 첫 번째 명령입니다.

    find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -print
    

    또는 이전 테스트를 모두 무효화전체적으로(이는 변경해야 할 사항이 무엇인지 알지 않는 한 개별적으로 부정하는 것과는 다릅니다. 비교하십시오.이것도착하다이것) 변경하여 무엇이 살아남는지 확인 -delete합니다 -print.

    find . ! \( ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile \) -print
    
  • 따옴표를 사용하지 않으면 ./folder1/*오류가 발생합니다. 쉘은 확장을 시도합니다 *.


-pathPOSIX 요구 사항하지만 -delete그리고 -regex아니오 -empty. 때로는 -exec rm {} +대신 사용할 수도 있지만 필요한 디렉토리가 삭제되지는 않습니다 -delete. 예, 디렉토리를 삭제할 수도 있지만 전체 요점 도 삭제되고 무효화됩니다.rmrmdirrm -rrm -r ./folder2./folder2/subfolder/onefile

-delete우리의 기본 접근 방식은 다음 사항 없이도(몇 가지 개선 사항을 적용하여) 구현할 수 있습니다.

find . -depth \
     ! -path  . \
     ! -path  ./folder1 \
     ! -path './folder1/*' \
     ! -path  ./file1 \
     ! -path  ./folder2 \
     ! -path  ./folder2/subfolder \
     ! -path  ./folder2/subfolder/onefile \
       \( \( ! -type d -exec rm {} + \) -o \( -type d -exec rmdir {} + \) \)

./folder2을 명시적으로 제외함으로써 ./folder2/subfolder명령이 의미 있는 종료 상태를 반환하도록 하고 이러한 디렉토리가 onefile존재하지 않더라도 보존합니다.

onefile존재하지 않는 디렉터리를 삭제하려면 추가 패턴을 사용하지 마세요. 연구 --ignore-fail-on-non-empty옵션 rmdir. POSIX에는 이 옵션이 필요하지 않습니다.

나는 마지막 명령이 빡빡하다고 말하지 않을 것입니다. 나는 그것이 -regex더 좋다.

답변2

이러한 잠재적으로 파괴적인 작업의 경우 수동으로 신중하게 수행합니다. 전체 경로가 포함된 모든 파일 목록을 가져오고, 남아 있어야 하는 파일을 편집하거나( grep -v또는 즐겨 사용하는 텍스트 편집기를 사용하여 누락된 파일이 있는지 확인합니다) ; 목록을 피드합니다( rm목록이 큰 경우 사용해야 할 수도 있음). xargs(1)공백이나 기타 특이한 문자가 포함된 파일 이름에 주의하세요!

관련 정보