限制查找命令的匹配数

Spa*_*sle 11 find

如果我想在找到一定数量的匹配项后停止 find 命令,我该怎么做?

背景是我在一个文件夹中有太多文件,我需要将它们随机放入单独的文件夹中,例如:

find -max-matches 1000 -exec mv {} /path/to/collection1 \+; 
find -max-matches 1000 -exec mv {} /path/to/collection2 \+; 
Run Code Online (Sandbox Code Playgroud)

这可以find单独做吗?如果不是,那么最简单的方法是什么?

Ste*_*itt 17

您可以实施新的测试以find使用-exec

seq 1 1000 |
find . -exec read \; -exec mv {} /path/to/collection1 +
Run Code Online (Sandbox Code Playgroud)

将找到的前 1000 个文件移动到/path/to/collection1.

其工作原理如下:

  • seq 1 1000输出 1000 行,通过管道输入find
  • -exec read读取一行,如果管道关闭则失败(当seq的输出被消耗时);
  • 如果前一个-exec成功,则-exec mv ...执行移动。

-exec ... +如您所料:read每次迭代运行一次,但find会累积匹配的文件并mv尽可能少地调用。

这取决于基于执行命令的退出状态find's-exec成功或失败的事实:read成功时,find继续处理上面给出的操作(因为默认运算符是“and”),失败时find停止。

如果你find支持这个-quit动作,你可以用它来提高效率:

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

没有它,find将测试每个文件,即使它只会保留 1000 个mv.

我假设它read可作为外部命令使用,并实现了 POSIX 规范read;如果不是这种情况,sh -c read可以改用。在这两种情况下,find都会为它检查的每个文件启动一个单独的进程。


Kus*_*nda 17

由于您find除了遍历目录树之外没有太多用途,因此我建议您直接使用 shell 来执行此操作。看到两者的变化zshbash下方。


使用zsh外壳

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

globbing 模式./**/*(-.D[1,1000])将匹配当前目录中或当前目录下的所有常规文件(或此类文件的符号链接),然后首先返回其中的 1000。在-.限制比赛进行到常规文件或符号链接到这些,而D行为就像dotglobbash(比赛隐藏的名字)。

这是假设在调用mv.

以上是非常低效的,因为它会扩展每个集合的全局。因此,您可能希望将路径名存储在一个数组中,然后移动它的切片:

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

mv $pathnames[1,1000]    /path/to/collection1
mv $pathnames[1001,2000] /path/to/collection2
Run Code Online (Sandbox Code Playgroud)

pathnames在创建数组时随机化数组(您提到要移动随机文件):

pathnames=( ./**/*(-.Doe['REPLY=$RANDOM']) )
Run Code Online (Sandbox Code Playgroud)

您可以在 中做类似的事情bash(除了您不能轻松地将 glob 匹配的结果改组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
Run Code Online (Sandbox Code Playgroud)


xen*_*oid 10

我不认为它可以只用find. 您可以使用以下内容:

find [... your parameters ...] -print0 | head -z -1000 | xargs -0 mv -t /path/to/collection
Run Code Online (Sandbox Code Playgroud)

-print0, -z, 和-0一起工作以确保即使文件名中有换行符也能正常工作。