특정 수의 일치 후에 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 1000
1000줄을 출력하고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 +
이것이 없으면 find
1000만 유지하더라도 모든 파일이 테스트됩니다 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
작동합니다 (숨겨진 이름과 일치).dotglob
bash
이는 호출 시 와일드카드 패턴의 확장으로 인해 생성된 명령이 너무 커지지 않는다고 가정합니다 mv
.
위의 접근 방식은 각 컬렉션의 전역 범위를 확장하므로 비효율적입니다. 따라서 경로 이름을 배열에 저장한 다음 그 안에서 슬라이스를 이동할 수 있습니다.
pathnames=( ./**/*(-.D) )
mv $pathnames[1,1000] /path/to/collection1
mv $pathnames[1001,2000] /path/to/collection2
배열을 생성할 때 배열을 무작위로 지정 하려면 pathnames
(무작위 파일을 이동하고 싶다고 언급하셨습니다):
pathnames=( ./**/*(-.Doe['REPLY=$RANDOM']) )
에서 비슷한 작업을 수행할 수 있습니다 (단 , 결과를 제공하는 경우를 제외하고 bash
는 에서 전역 일치 결과를 쉽게 망칠 수 없으므로 해당 단계를 건너뛰겠습니다).bash
shuf
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
파이프가 닫히면 끝까지 파이프가 종료됩니다.-exec
find
head