如果我希望 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
太多其他用途,因此我建议直接使用 shell 来执行此操作。请参阅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
in 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