많은 폴더가 포함된 디렉터리가 있고 100개 이상의 파일이 포함된 폴더를 이동하고 싶습니다.
나는 이것을 할 생각입니다 :
find . -type d | while read d; do if
이제 이것은 나에게 까다로운 부분입니다.
각 디렉토리로 이동하여 100개 이상의 파일이 포함되어 있는지 확인하기 위해 for를 수행합니까? 그렇다면 어떻게 해야 합니까?
for f in *; do cd $f; ll | wc; ?
디렉터리 내부의 총 파일 수를 가져온 다음 해당 디렉터리를 이동하는 방법이 약간 혼란스럽습니다(100개 이상의 파일이 포함되어 있음).
답변1
디렉토리에서 숨겨지지 않은 이름의 수를 계산하려면 dir
다음을 사용할 수 있습니다.
set -- dir/*
그러면 *
디렉터리의 glob이 확장되고 위치 인수가 결과 이름으로 설정됩니다. 패턴이 일치하면아무것이면 개수는 입니다 $#
.
특정 최상위 디렉터리의 모든 디렉터리를 반복하려면 top-dir
각 디렉터리의 이름 수를 세고 이름이 100개가 넘는 디렉터리에 대해 일부 작업을 수행합니다.
for subdir in top-dir/*/; do
set -- "$subdir"/*
if [ -e "$1" ] && [ "$#" -gt 100 ]; then
# do something to "$subdir"
fi
done
bash
셸 에서 nullglob
셸 옵션을 설정하면 명령이 이름과 일치하는지 여부를 확인할 필요가 없습니다 set
(일치하는 항목이 없으면 패턴이 확장되지 않은 채로 남아 있는 것이 아니라 완전히 제거되기 때문입니다).
shopt -s nullglob
for subdir in top-dir/*/; do
set -- "$subdir"/*
if [[ $# -gt 100 ]]; then
# do something to "$subdir"
fi
done
또한 dotglob
셸 옵션을 설정하면 코드의 모든 패턴이 숨겨진 이름과도 일치하게 됩니다.
위의 코드 조각에서 "do some to "$subdir"
" 주석은 해당 하위 디렉터리에서 수행해야 하는 작업으로 대체될 수 있습니다. 예를 들어, 방해가 되지 않는 곳으로 이동하려면 다음을 사용하세요.
mv "$subdir" some/other/dir
이렇게 하면 해당 파일이 디렉토리로 이동됩니다 some/other/dir
.
답변2
각 디렉토리를 차례로 순환하고, 포함된 파일 수를 세고, 어딘가로 이동할 수 있습니다. 예를 들어,
for dir in ./*/
do
count=$(find "$dir" -maxdepth 1 -type f -printf "x\n" | wc -l) # Count the number of files in this subdirectory
[ $count -gt 100 ] && echo mv "$dir" # Output a message if we have enough
done
>
프롬프트에 직접 입력하거나(첫 번째 줄부터 마지막 줄까지 보조 프롬프트가 표시됨) 스크립트 파일에 저장하고 실행할 수 있습니다.
답변3
그리고 zsh
:
mv -- *(/Fe['()(($# > 100)) $REPLY/*(N^-/)']) /dest/
/dest/
숨겨지지도 않고 유형도 아닌 항목이 100개 이상 포함된 현재 작업 디렉터리의 숨겨지지 않은 하위 디렉터리 로 이동합니다. 목차(내 생각에는 당신이 말하는 것 같아요문서).
이는 다음을 활용합니다.zsh
글로벌 예선( (/Fe...)
이상 (N...)
) 이름 이외의 기준에 따라 일치하는 파일을 추가로 선택합니다.
/
:파일 형식 선택목차오직. 여기서 ( glob 과 반대로*/
) 유형이 결정됩니다.앞으로여기서는 심볼릭 링크를 이동하면 심볼릭 링크가 끊어지는 경우가 많기 때문에 심볼릭 링크 확인이 더 바람직할 수 있습니다.F
: 선택하다가득한최적화된 파일(디렉토리의 경우 이는 비어 있지 않은 디렉토리를 의미함)e[code]
: 현재 고려 중인 파일이 포함된 위치의 해석을 기준으로code
선택합니다 .$REPLY
여기 code
있어 ()(($# > 100)) $REPLY/*(N^-/)
.
() <body> <args>
인라인 함수입니다. 여기서 body( (($# > 100))
)는 매개변수의 개수가 100보다 큰지 확인합니다. 매개변수는 $REPLY/*(N^-/)
glob의 확장입니다. 다시 사용하세요.글로벌 예선:
N
: nullglob: glob이 다음으로 확장됩니다.아니요일치하는 파일이 없으면 오류 대신 인수가 전혀 없습니다.^
: 다음 한정자를 무효화합니다.-/
/
-
다음 한정자(여기/
)를 적용 한다는 점 을 제외하면 위와 유사합니다.뒤쪽에심볼릭 링크 해결. 그래서 여기서 우리는 파일 수를 세고 있습니다.아니요유형목차심볼릭 링크가 해결된 후.^-/
바꿔서.
계산하시면 됩니다정기적인파일(소켓, fifo, 디렉터리, 심볼릭 링크 등 기타 모든 유형의 파일 제외) 또는-.
일반 파일 및 일반 파일에 대한 심볼릭 링크만 해당됩니다.
숨겨진 디렉터리/파일도 고려하려면 D
한정자(외부 및 내부 glob 중 하나 또는 모두)를 추가하세요.
하위 디렉터리의 파일 수를 반복적으로 계산하려면 두 번째 디렉터리를 다음 *
으로 바꾸세요 **/*
(또는 ***/*
디렉터리 트리를 내려갈 때 심볼릭 링크를 통과하세요).
다음으로 변경하여 더욱 최적화할 수 있습니다 code
.
()(($#)) $REPLY/*(NoN^-/[101])
oN
이는 순서에 신경 쓰지 않고 glob이 101번째 일치 파일로만 확장되므로 파일 정렬을 비활성화 하는 데 사용됩니다 . 우리는 파일이 존재하는지 (($#))
(0이 아닌 인수 개수) 테스트만 합니다.
¹ 거기에 있는 여러 항목이 동일한 파일을 참조할 수 있다는 점에 유의하십시오. 예를 들어, 하드 또는 심볼릭 링크가 함께 연결된 경우입니다. 고유 수량 계산문서다른 운동이겠지
답변4
셸 접근 방식 외에도 신중하게 제작된 명령의 파이프라인을 사용하여 find
검사할 폴더/파일을 선택하고 이를 최종 결과를 필터링하는 Awk 스크립트에 공급하여 가능한 한 적은 실행으로 실행할 수 있습니다. 실제 명령은 xargs
. mv
쉘 스크립트일 수도 있지만 일반적으로 Awk가 텍스트 처리에 더 좋고 빠릅니다.
다음은 줄 바꿈이 포함된 파일 이름을 지원하기 위해 널로 구분된 I/O GNU 도구를 처리하기 위한 지침을 사용합니다.
find . -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' -print0 \
| LC_ALL=C gawk -F/ -v RS='\0' -v ORS='\0' \
'{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; print $2}}' \
| xargs -r0 mv -t dest/
이 파이프라인은 처리 중에 버퍼링을 수행하지 않으므로 기본적으로 폴더 및 파일 수에 영향을 받지 않으므로 순수 셸 솔루션보다 리소스를 덜 요구합니다.
n>100
스크립트 의 비교를 참고하세요 awk
. 여기에서 원하는 만큼 임계값을 조정할 수 있습니다.
파이프라인은 "naked" 를 사용하기 때문에 확인할 폴더가 포함된 디렉터리에서 실행될 것으로 예상됩니다 find .
. 그러나 기본적으로 현재 디렉터리를 사용하는 사용자 정의 셸 변수를 통해 시작 디렉터리를 제공할 수 있도록 find .
앞에 코드 조각을 추가하여 쉽게 일반 디렉터리로 만들 수 있습니다.cd -- "${topdir:-.}" &&
$topdir
.
BSD 도구를 사용하는 이러한 파이프와 동등한 항목은 다음과 같습니다(BSD 도구의 고유한 제한으로 인해 파일 이름에 줄 바꿈 지원 제외).
find -E . -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' \
| LC_ALL=C awk -F/ -v q=\' \
'{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; gsub(q, q"\\"q q, $2); print q$2q}}' \
| xargs sh -c '${1:+mv -- "$@" dest/}' --
POSIX에서 요구하는 파일 이름에 존재할 수 있는 구분 기호(문자)를 참조하기 위한 다양한 -print0
, -z
옵션 및 스크립트의 추가 조작을 제외하면 기본적으로 GNU 도구 버전과 동일합니다 .-0
gsub()
awk
" ' <space>
xargs
후자의 파이프는 확인된 경로(폴더 및 파일)에 개행 문자가 포함되어 있지 않다고 가정할 때 모든 BSD 시스템에서 제대로 작동합니다.
POSIX 준수에 관해 말하면 BSD 도구 파이프라인은 명령을 제외한 모든 POSIX 시스템에서도 작동해야 합니다 . POSIX에는 AND 절이 find
없기 때문입니다 . POSIX에 해당하는 내용은 다음과 같습니다.-maxdepth
-regex
find
# replace the find command of the BSD tools version, up to and including the trailing backslash character
find . \( -path '*/*/*' ! -path '*/*/*/*' -type f \) -o \( -path '*/*' ! -path '*/*/*' -type d \) ! -name '.*' \
이 find
표현식 awk
은 또한 스크립트 작업을 단순화하고 .
디렉터리 계층의 세 번째 수준(수준 1이 있음)과 두 번째 수준의 디렉터리에서 일반 파일을 선택하도록 설계되었습니다. BSD와 GNU에는 더 강력한 조항이 없기 때문에 find
조항 게임에서도 동일한 결과를 얻었습니다 -path
.
마지막으로, 이러한 파이프라인은 ! -name '.*'
to 절을 통해 숨겨진 파일을 명시적으로 무시하며 find
,일반 파일pass -type f
절(예: 심볼릭 링크 제외)은 귀하의 질문에 따르면 가장 현명한 옵션인 것처럼 보이지만 숨겨진 파일 및/또는 하위 폴더, 심볼릭 링크 및 특수 파일(이름이 파이프, 이름이 소켓 등)을 정말로 고려하고 싶다면 )가 폴더 내부에 존재할 수 있으며 해당 절을 제거하거나 find
명령의 추가 절을 사용하여 미세 조정할 수 있습니다. 후자의 경우 make는 find
항상 별도의 폴더 이름 1awk
을 생성합니다 . 이러한 별도의 이름은 스크립트가 후속 이름이 이전 이름 2 와 다른 폴더에 있음을 감지하는 데 사용하는 "신호"이기 때문입니다 .
1. 레벨 2에만 해당
2. 더 나은 성능을 위해 문자열 비교 대신 참/거짓 테스트를 사용했습니다. 스크립트 d
의 변수를 참조하세요.awk