모든 빈 디렉토리와 .DS_Store
Mac 생성 파일만 포함된 디렉토리를 삭제하는 쉘 스크립트를 작성하려고 합니다. 나는 전자를 쉽게 할 수 있다
find -depth -type d -empty
하지만 다음이 포함된 디렉토리를 찾는 방법을 모르겠습니다.오직 .DS_Store
.
자체 재귀 검색 기능을 작성하지 않고도 이를 수행할 수 있는 쉬운 방법이 있습니까?
답변1
POSIX sh + 찾기
이는 POSIX find 및 POSIX sh에만 의존하는 솔루션입니다. 모든 디렉터리를 나열한 다음 이름이 지정된 항목만 포함하는 디렉터리를 필터링합니다 .DS_Store
.
find . -type d -exec sh -c '
cd "$0" &&
for x in * .[!.]* ..?*; do
if [ "$x" = ".DS_Store" ]; then continue; fi;
if [ -e "$x" ] || [ -L "$x" ]; then exit 1; fi;
done' {} \; -print
- 나는
find
모든 디렉토리를 재귀적으로 열거하는 데 사용합니다. sh
각 디렉터리에서 일부 셸 코드를 실행하는 호출을 수행합니다 .- 이
for
루프는 디렉터리의 모든 파일을 열거합니다. - 루프 본문을 건너뜁니다
.DS_Store
. - 이 세 가지 패턴 각각이 어떤 파일과도 일치하지 않으면 변경되지 않은 상태로 유지됩니다.
[ -e "$x" ] || [ -L "$x" ]
깨진 심볼릭 링크를 포함하여 모든 파일을 캡처합니다. 일치하지 않는 유일한 이유는 패턴이 동일하게 유지되기 때문입니다. exit 1
따라서 파일이 아닌 다른 파일이 있으면 쉘 조각이 실행되고.DS_Store
, 그렇지 않으면 성공 시 0이 반환됩니다.- 이름 인쇄 이외의 작업을 수행하려면
-print
다음으로 변경하세요.-exec …
지쉬
zsh의 솔루션은 다음과 같습니다. echo
실행하려는 명령으로 변경하십시오 .
setopt extended_glob
echo **/*(/DNe\''a=($REPLY/^.DS_Store(DNY1)); ((!#a))'\')
**/*
모든 파일을 재귀적으로 열거합니다.- 와 함께글로벌 예선
/
,**/*(/)
모든 디렉토리를 재귀적으로 열거합니다. N
glob 한정자는 일치하는 항목이 없는 경우 빈 목록을 얻도록 보장합니다(zsh는 기본적으로 오류 신호를 보냅니다).- glob 한정자는
D
도트 파일을 포함시킵니다. - glob 한정자는 일치하는 각 파일 이름에 대해 작동하며 일치 항목을 성공적인 파일 이름으로 제한합니다. 변수를 사용하여 파일 이름을 참조할 수 있습니다.
e\''CODE'\'
CODE
CODE
CODE
$REPLY
^.DS_Store
호출되지 않은 파일과 일치합니다.DS_Store
.- 그게 다야암호
.DS_Store
0이 아닌 파일 개수와 일치하는 항목 으로 일치를 제한합니다 . - glob 한정자는
Y1
일치 항목 수를 하나로 제한합니다(이는 효율성 향상일 뿐입니다).
파이썬
다음은 Python의 솔루션입니다(2와 3 모두에서 작동함). 한 줄로 압축되어 있음에도 불구하고 구조는 매우 명확합니다.
python -c 'import os; print("\n".join([path for path, dirs, files in os.walk(".") if dirs == [] and files in ([], [".DS_Store"])]))'
os.walk
해당 인수 아래에 디렉터리 목록을 반복적으로 반환합니다. 각 디렉터리에 대해path
(디렉터리 경로),dirs
(하위 디렉터리 목록) 및files
(디렉터리 자체가 디렉터리가 아닌 디렉터리의 파일 목록)을 포함하는 트리플을 생성합니다 .[… for … in os.walk(…) if …]
os.walk
필터링 된 결과if
이 절은 하위 디렉토리가 없고 외부에 파일이 없는 경우에만 요소를 보존합니다.DS_Store
.- 스크립트는 중간 줄 바꿈과 마지막 줄 바꿈 사이에 줄 바꿈이 포함된 허용된 요소를 인쇄합니다.
답변2
간단한 해결 방법: 먼저 해당 파일을 모두 삭제합니다.
find <path> -type f -name "*.DS_Store" -delete
그런 다음 빈 디렉토리를 삭제하십시오.
의견을 기반으로 업데이트: 이러한 파일만 포함된 디렉토리만 삭제하려면 다음과 같은 것이 필요합니다(경고: 전혀 테스트되지 않았으므로 더 많은 참여가 필요하더라도 놀라지 않을 것입니다).
find <path> -type d | while read dir; do
if ! ls --ignore=*.DS_Store $dir; then
rm -rf $dir
fi
done
복잡한 부분에 대한 설명:
ls --ignore=*.DS_Store $dir
.DS_Store로 끝나지 않는 $dir의 파일은 인쇄되어야 합니다. 그렇지 않은 경우 표현식은 False이고 if 블록이 실행됩니다.
답변3
맥의 경우:
find . -type d | while read dir; do
# Found empty dir if it only has:
# ., ..
# ., .., .DS_Store
if [ "$(ls -am "$dir" | sed -E 's/^., ..$|^., .., .DS_Store$//')" == "" ]; then
echo rm -R "\"$dir\""
fi
done
답변4
더 많은 보호 기능을 갖춘 또 다른 POSIX 변형:
find . -type d -exec sh -c '
for dir do
contents=$(ls -AFq "$dir") &&
case $contents in
("" | .[dD][sS]_[sS][tT][oO][rR][eE]) printf "%s\n" "$dir"
esac
done' sh {} +
여기:
ls
디렉터리 내용을 나열할 수 없거나 디렉터리 내용을 찾을 수 없는 경우 문제를 해결할 수 있습니다.- 를 사용함으로써
ls -F
우리는 디렉토리가.DS_Store
정기적인파일(-F
다른 유형의 파일에 접미사 추가) - 예를 들어 를 사용하면
-q
파일 이름에 대한 오탐을 방지할 수 있습니다.$'.DS_Store\n\n'
- Macos에서는 파일 이름이 대소문자를 구분하지 않는다고 생각하기 때문에 대소문자를 구분하지 않고 일치시킵니다.
이에 상응하는 내용은 zsh
다음과 같습니다.
empty() {
set -o localoptions -o extendedglob
ERRNO=0
() ((!ERRNO && !$#)) $REPLY/(#i){^.ds_store(NDY1),.ds_store(N^.)}
}
print -rC1 -- **/*(ND/+empty)
이제 이러한 디렉터리를 삭제하려는 경우 해당 디렉터리 dirA
에 파일만 포함되어 dirB
있고 dirA/dirB
비어 있거나 .DS_Store
파일만 포함되어 있는 경우가 있습니다. 그러면 삭제 후 비어 있어도 dirA
신고하지 않습니다.dirA/dirB
이 문제를 해결하려면 여기를 사용할 수 있지만 빈 디렉토리는 발견되는 즉시 삭제되기 때문에 -depth
사용할 수 없습니다. 이는 중요하므로 다음을 사용합니다 .-exec ... {} +
-exec ... {} ';'
find . -depth -type d -exec sh -c '
contents=$(ls -AFq "$1") &&
case $contents in
("" | .[dD][sS]_[sS][tT][oO][rR][eE]) exec rm -rf "$1"
esac' sh {} ';'