【问题标题】:Get x files from a directory (batch, windows)从目录中获取 x 个文件(批处理、windows)
【发布时间】:2012-07-07 03:25:43
【问题描述】:

我有一个包含数千个文件的目录。

有没有办法只获取 x 个文件? (最好用于批处理文件)

但是(这就是为什么它不是我已经找到答案的相同问题之一)

无需“dir”命令处理整个目录内容 或者使用“for”循环。

虽然“for”循环(即 for %%a in (.txt) do [something])与计数器相结合会起作用,但这样做需要相当长的时间。我只能假设“for”循环中的文件“集”(即(.txt))首先获取整个内容并对其进行迭代。

如果我可以在 x 返回值后停止“dir”命令,那会很好,但我想不出办法来做到这一点(有没有可能,或者我只是回答了我的问题但缺乏知识做它的编码?)。

提前致谢。

【问题讨论】:

  • for 命令说:关于我表现不佳的报告是夸大其词...而dir 回复:我也是。跨度>
  • 1.- 任何获取目录内容的方法在低级别都会以非常大的块读取目录,可能是只有一个磁盘访问权限的完整目录,或者根本没有磁盘访问权限,因为它往往在磁盘缓存中。 2.- 任何返回目录内容的方法都有隐式排序,要求操作系统读取完整目录。为了加快阅读速度,您需要指定不对目录进行排序。如果您发现 dirfor 的差异可能是由于之前的内容缓存。
  • @PA.-你提出了一个有趣的观点。这让我想知道,像 FOR 这样的命令如何能够在开始迭代后看到添加或更改的条目?
  • @dbenham ...你是对的,这很有趣。也许它检测到目录内容的变化并重新读取它?

标签: windows file batch-file directory cmd


【解决方案1】:

您对 FOR 循环有误。只要您不使用 /R 选项,它就非常快。 (我在 XP、Vista 和 Windows 7 上测试过)

我创建了一个包含 20,000 个文件的测试目录并运行以下脚本 - 它打印出第一个 10 个文件并在眨眼间完成:

@echo off
set n=0
for %%F in (*) do (
  echo %%F
  set /a "n+=1, 1/(10-n)" 2>nul || goto :break
)
:break

我通过故意除以 0 来检测何时达到 10 个文件以生成错误。我本可以使用延迟扩展并测试我的计数器的值,但如果文件名包含 ! 字符并且启用了延迟扩展,%%F 将无法正确打印。

因此与您的看法相反,一个简单的 FOR 不会在迭代循环之前收集所有值。

您可能对 FOR /F 变体感到困惑:for /f "delims=" %%F in ('dir /b /a-d *') do ...。在这种情况下,DIR 命令在其自己的 shell 中执行,并且在任何 FOR 迭代之前获得整个结果集

使用 FOR 解决方案需要注意的一件事 - FOR /R 选项似乎有可能减慢速度。我修改了上面的脚本以使用 /R 选项,然后在我的C:\ root 上运行它。它很快打印了它找到的前 10 个文件,但随后似乎挂了很长时间,然后终于完成了。我不知道如何证明这一点,但我认为 FOR 循环正在浪费时间迭代我的 C: 驱动器上的所有目录,即使它没有对它们做任何事情。

编辑

我刚刚阅读了 PA. 对原始问题的评论。他有一个很好的观点——在某种程度上,FOR 和 DIR 都必须读取整个目录,以便执行隐式排序操作以正确顺序返回文件。但是,他也指出这是在非常低的水平上完成的。我的观点是,FOR 命令在开始迭代之前不需要等待所有值都返回(或就此而言跳出循环)。

编辑 2 FOR /F 行为可以是福也可以是祸。对于这个应用程序,这是一个诅咒。但有时您在处理目录时会更改目录的内容。 FOR /F 变体可防止您看到命令启动后发生的任何更改。简单的 FOR 命令有时可以在开始迭代后看到目录内容的变化。

FOR 命令以块的形式缓冲迭代 - 它会迭代它在缓冲区中的所有文件,但是当缓冲区为空并返回操作系统以获取下一个块时,它可以看到已经发生的更改到目录。这是一个演示该行为的测试脚本:

@echo off
md myTemp
for /l %%N in (1001 1 6000) do echo a>myTemp\%%N.txt
for /f %%N in ('dir /b myTemp\* ^| find /c /v ""') do Echo Starting with %%N files
set cnt=0
(
  for %%F in (myTemp\*) do (
    echo %%F
    del /q myTemp\* 2>nul
    set /a cnt+=1
  )
)> test_for.out
echo Only %cnt% files were listed by FOR
echo(

for /l %%N in (1001 1 6000) do echo a>myTemp\%%N.txt
for /f %%N in ('dir /b myTemp\* ^| find /c /v ""') do Echo Starting with %%N files
set cnt=0
(
  for /f %%F in ('dir /b /a-d myTemp\*') do (
    echo %%F
    del /q myTemp\* 2>nul
    set /a cnt+=1
  )
)> test_for_f.out
echo All %cnt% files were listed by FOR /F

rd myTemp

结果如下:

Starting with 5000 files
Only 743 files were listed by FOR

Starting with 5000 files
All 5000 files were listed by FOR /F

两个循环都从 5000 个文件开始,并在第一次迭代后删除所有文件。 FOR /F 仍会处理启动时目录中的所有 5000 个文件名。 FOR 只处理 743 个适合缓冲区的文件名。

注意:我在 Windows 7 上获得了上述结果。我还在 Vista 和 XP 上进行了测试,除了在 FOR 循环中仅列出 35 个文件而不是 743 个文件之外,结果都相同。

【讨论】:

  • @PA。 - 谢谢,但我将不得不改变它。我试图重现 FOR 命令,看到中途发生的变化,但我失败了。我知道这是一个问题,但它似乎比我想象的要复杂。我还记得看到一个帖子处理这个问题,但我找不到它...... Aaargh。
  • 哦,我只是认为你的话是理所当然的。 :) 我热切地等待你的最终解释。
  • @PA。 - 我用 XP 测试的结果更新了我的答案。所有现代版本的 Windows 似乎都表现相同。只是缓冲区的大小似乎有所不同。
  • @dbenham - 感谢您的详细回答。 :) 我没有考虑使用“dir”来获取所需文件集的 FOR /F 变体,这正是因为“dir”的使用。但是,由于您的良好解释,我意识到我错过了以下信息,这会将我的问题变成另一个话题(我认为):文件所在的目录不在我的本地机器上,而是在一个 NAS。所以 FOR 命令正在等待来自 NAS 控制器的结果来处理。这就是为什么我认为 FOR 与 DIR 完全一样。所以我想知道我怎样才能加快速度(如果可能的话)
  • ...这肯定取决于 NAS 制造商(我想),我必须获得一些 Unix/Linux 知识。 ^.^
猜你喜欢
  • 2013-06-08
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-04
相关资源
最近更新 更多