限制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 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其作用类似于dotglobin 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之前进行评估,并且在关闭管道时,到头的管道将导致退出。-execfindhead

相关内容