find 명령과 일치하는 항목 수 제한

find 명령과 일치하는 항목 수 제한

특정 수의 일치 후에 find 명령을 중지하려면 어떻게 해야 합니까?

배경은 폴더에 파일이 너무 많아서 무작위로 다음과 같이 별도의 폴더에 넣어야 한다는 것입니다.

find -max-matches 1000 -exec mv {} /path/to/collection1 \+; 
find -max-matches 1000 -exec mv {} /path/to/collection2 \+; 

이거 혼자 할 수 있나요 find? 그렇지 않다면 가장 쉬운 방법은 무엇입니까?

답변1

find다음을 사용 하여 새로운 테스트를 구현할 수 있습니다 -exec.

seq 1 1000 |
find . -exec read \; -exec mv {} /path/to/collection1 +

발견된 처음 1000개의 파일을 으로 이동합니다 /path/to/collection1.

작동 방식은 다음과 같습니다.

  • seq 1 10001000줄을 출력하고 find;
  • -exec read라인을 읽고 파이프가 닫히면( seq출력이 소비된 경우) 실패합니다.
  • 이전 작업이 -exec성공한 경우 -exec mv ...이동이 수행됩니다.

-exec ... +예상대로 작동합니다. read반복당 한 번 실행되지만 find일치하는 파일을 누적하고 mv가능한 한 적은 수를 호출합니다.

이는 다음 사실에 따라 달라집니다.find성공 -exec또는 실패는 실행 명령의 종료 상태에 따라 달라집니다. read성공하면 find위에 제공된 작업을 계속 처리하고(기본 연산자는 "and"이므로) 실패하면 find중지합니다.

find이 작업을 지원 하는 경우 -quit이를 사용하여 효율성을 향상할 수 있습니다.

seq 1 1000 |
find . \( -exec read \; -o -quit \) -exec mv {} /path/to/collection1 +

이것이 없으면 find1000만 유지하더라도 모든 파일이 테스트됩니다 mv.

read외부 명령으로 사용할 수 있다고 가정 하고 구현했습니다.POSIX 사양read; 그렇지 않은 경우에는 sh -c read이를 대신 사용할 수 있습니다. 두 경우 모두 find검사하는 각 파일에 대해 별도의 프로세스가 시작됩니다.

답변2

디렉토리 트리를 탐색하는 것 외에는 별로 쓸모가 없으므로 find이 작업을 수행하려면 쉘을 직접 사용하는 것이 좋습니다. zsh아래 두 가지 변형을 모두 참조하세요 bash.


zsh쉘을 사용하다

mv ./**/*(-.D[1,1000]) /path/to/collection1    # move first 1000 files
mv ./**/*(-.D[1,1000]) /path/to/collection2    # move next 1000 files

와일드카드 패턴은 ./**/*(-.D[1,1000])현재 디렉터리 안이나 아래에 있는 모든 일반 파일(또는 해당 파일에 대한 심볼릭 링크)을 일치시키고 그 중 1000번째 파일을 반환합니다. 일반 파일 또는 해당 파일에 대한 심볼릭 링크에 대한 일치를 제한 -.하고 에서처럼 D작동합니다 (숨겨진 이름과 일치).dotglobbash

이는 호출 시 와일드카드 패턴의 확장으로 인해 생성된 명령이 너무 커지지 않는다고 가정합니다 mv.

위의 접근 방식은 각 컬렉션의 전역 범위를 확장하므로 비효율적입니다. 따라서 경로 이름을 배열에 저장한 다음 그 안에서 슬라이스를 이동할 수 있습니다.

pathnames=( ./**/*(-.D) )

mv $pathnames[1,1000]    /path/to/collection1
mv $pathnames[1001,2000] /path/to/collection2

배열을 생성할 때 배열을 무작위로 지정 하려면 pathnames(무작위 파일을 이동하고 싶다고 언급하셨습니다):

pathnames=( ./**/*(-.Doe['REPLY=$RANDOM']) )

에서 비슷한 작업을 수행할 수 있습니다 (단 , 결과를 제공하는 경우를 제외하고 bash는 에서 전역 일치 결과를 쉽게 망칠 수 없으므로 해당 단계를 건너뛰겠습니다).bashshuf

shopt -s globstar dotglob nullglob

pathnames=()
for pathname in ./**/*; do
    [[ -f $pathname ]] && pathnames+=( "$pathname" )
done

mv "${pathnames[@]:0:1000}"    /path/to/collection1
mv "${pathnames[@]:1000:1000}" /path/to/collection2
mv "${pathnames[@]:2000:1000}" /path/to/collection3

답변3

혼자서는 할 수 없을 것 같아요 find. 다음과 같은 것을 사용할 수 있습니다.

find [... your parameters ...] -print0 | head -z -1000 | xargs -0 mv -t /path/to/collection

-print0, -z-0함께 사용하면 파일 이름에 줄 바꿈이 있어도 모든 것이 올바르게 작동하는지 확인할 수 있습니다.

답변4

Stephens의 답변 264963은 아마도 내 사용 사례에 가장 적합할 것입니다. 그러나 이 질문의 사용 사례에 대한 간단한 해결 방법은 find 및 head만 있으면 됩니다.

find . [checks] -print -exec ... | head

(적어도 CentOS 8에서는) 이전에 평가되며 -print파이프가 닫히면 끝까지 파이프가 종료됩니다.-execfindhead

관련 정보