我做了一些测试forfiles
- 这是结果......
目的和范围
forfiles
这里的测试用例旨在证明在迭代所有(子)项之前是否完成了给定目录(树)的枚举。
下面的列表显示了这里的测试涵盖了哪些模式:
- 文件模式 (
/M
) 始终是*.txt
;
- 文件模式 (
/M
) 仅匹配文件,但不匹配目录;
- 总是有一个根搜索路径给定(
/P
);
- 在正文 (
/C
) 中,仅使用内部cmd.exe
命令(以 为前缀cmd /C
);
- 迭代几个和一百个文件的非递归操作;
- 递归操作 (
/S
) 仅迭代几个目录;
- 递归操作 (
/S
) 迭代目录层次结构,深度为一级;
- 文件年龄过滤器 (
/D
) 根本没有使用;
- 目录(树)内容仅在某个迭代期间修改一次;
- 文件(内容)不会被修改,因此不会检测到大小和日期/时间的变化;
测试设置
所有测试均在 NTFS 格式磁盘上执行。(这可能是所有文件都forfiles
按字母顺序枚举的原因。)
操作系统是 Microsoft Windows 7 64 位(版本 6.1.7601)。
先决条件
在执行相应的命令行之前,需要提前促进各个测试步骤中描述的所需目录树。
使用的根目录中不得有任何其他文件或目录D:\Data
。
forfiles /S
, 递归的
对于这里的测试用例,必须建立以下目录树:
D:\Data\
+---sub1\
| file1.txt
| file2.txt
| file3.txt
+---sub2\
| file1.txt
| file2.txt
| file3.txt
+---sub3\
| file1.txt
| file2.txt
| file3.txt
+---sub4\
| file1.txt
| file2.txt
| file3.txt
+---sub5\
file1.txt
file2.txt
file3.txt
我使用以下代码行进行设置:
@(pushd D:\Data
md sub1 & pushd sub1 & rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt & popd
md sub2 & pushd sub2 & rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt & popd
md sub3 & pushd sub3 & rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt & popd
md sub4 & pushd sub4 & rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt & popd
md sub5 & pushd sub5 & rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt & popd
rd /S /Q sub6
popd) > nul 2>&1
我的意图是等到file2.txt
文件夹中的项目sub3
被迭代,然后完成以下任务:
- in
sub3
,目前正在迭代,
- 删除
file1.txt
(已经迭代);
- 删除
file3.txt
(尚未迭代);
- 创建
file4.txt
(新项目,因此尚未迭代);
- 删除容器
sub1
(已经迭代);
- 删除容器
sub4
(尚未迭代);
- 通过重命名来更改
sub2
(已经迭代)的内容;file2.txt
file4.txt
- 通过重命名来更改
sub5
(尚未迭代)的内容;file2.txt
file4.txt
- 创建容器
sub6
(新项目,因此尚未迭代),file4.txt
在里面创建;
对于所有迭代项,完整路径会回显到命令提示符。
如果枚举在遍历所有项之前完成,则预期会输出原始目录树,因此不应显示任何修改。
现在让我们看看会发生什么;这是要执行的命令行:
forfiles /S /P "D:\Data" /M "*.txt" /C "cmd /C (if @relpath==\".\sub3\file2.txt\" (del file1.txt & del file3.txt & rem.> file4.txt & rd /S /Q ..\sub1 & rd /S /Q ..\sub4 & ren ..\sub2\file2.txt file4.txt & ren ..\sub5\file2.txt file4.txt & md ..\sub6 & rem.> ..\sub6\file4.txt)) & echo @path"
输出如下:
"D:\Data\sub1\file1.txt"
"D:\Data\sub1\file2.txt"
"D:\Data\sub1\file3.txt"
"D:\Data\sub2\file1.txt"
"D:\Data\sub2\file2.txt"
"D:\Data\sub2\file3.txt"
"D:\Data\sub3\file1.txt"
"D:\Data\sub3\file2.txt"
"D:\Data\sub3\file3.txt"
ERROR: The system cannot find the file specified.
"D:\Data\sub5\file1.txt"
"D:\Data\sub5\file3.txt"
"D:\Data\sub5\file4.txt"
我们可以清楚地看到,这显然不是原始目录树。
似乎在迭代之前枚举了树中的目录,但是一旦迭代到达那里,就会枚举每个目录内容。(至少对于手头的小树来说是这样的;但是,具有高层次深度的大树的目录可能在迭代之前没有被完全枚举。)
删除sub1
和修改内容sub2
没有注意到。一旦sub4
达到,就会返回一个错误,因为在迭代期间sub3
,sub4
已被删除。sub5
检测到内容的修改。sub6
,它是在迭代过程中创建的sub3
,根本无法识别。
forfiles
, 非递归的
forfiles
如果没有该选项,则使用/S
平面目录树:
D:\Data\
file1.txt
file2.txt
file3.txt
这是使用以下代码片段创建的:
@(pushd D:\Data
rem.> file1.txt & rem.> file2.txt & rem.> file3.txt & del file4.txt
popd) > nul 2>&1
对于测试,forfiles
正文中的命令行检查当前文件是否为file2.txt
; 如果是,file1.txt
则file3.txt
删除并file4.txt
创建新的。当前文件回显到命令提示符。
要执行的命令行是:
forfiles /P "D:\Data" /M "*.txt" /C "cmd /C (if @fname==\"file2\" (del file1.txt & del file3.txt & rem.> file4.txt)) & echo @file"
输出是:
"file1.txt"
"file2.txt"
"file3.txt"
这表明在遍历文件之前已经枚举了整个目录内容。
但是,为了证明上述假设,让我们进行一些更密集的测试。
这一次,我们使用了一百个文件:
D:\Data\
file0.txt
file1.txt
file2.txt
...
file99.txt
这些是使用以下代码创建的:
@(pushd D:\Data
del file100.txt & del file999.txt
for /L %%N in (0,1,99) do (echo.%%N> file%%N.txt)
popd) > nul 2>&1
在这个实验中,我们重命名file99.txt
为file999.txt
as soon as file1.txt
is iterated。
要执行的命令行是:
forfiles /P "D:\Data" /M "*.txt" /C "cmd /C (if @fname==\"file1\" (ren file99.txt file999.txt)) & echo @file"
输出是:
"file0.txt"
"file1.txt"
"file10.txt"
"file11.txt"
...
"file98.txt"
"file999.txt"
我们收到一个包含 100 个文件的列表,它反映了重命名,这意味着我们没有读取原始文件列表。因此,在迭代开始之前没有完成枚举。
这里我们再次使用上面的 100 个文件。
在这个实验中,我们重命名file99.txt
为file100.txt
as soon as file1.txt
is iterated。
要执行的命令行是:
forfiles /P "D:\Data" /M "*.txt" /C "cmd /C (if @fname==\"file1\" (ren file99.txt file100.txt)) & echo @file"
输出是:
"file0.txt"
"file1.txt"
"file10.txt"
"file11.txt"
...
"file98.txt"
所以现在我们收到一个只有 99 个文件的列表,没有file99.txt
和file100.txt
. 似乎最后一个文件的枚举是在文件重命名之后完成的,但file100.txt
没有显示,因为它会违反字母顺序(它应该出现在 之后file10.txt
,但该位置附近的文件似乎已经被枚举)。
我们再次使用上述 100 个文件。
在这个实验中,我们重命名file0.txt
为file999.txt
as soon as file1.txt
is iterated。
要执行的命令行是:
forfiles /P "D:\Data" /M "*.txt" /C "cmd /C (if @fname==\"file1\" (ren file0.txt file999.txt)) & echo @file"
输出是:
"file0.txt"
"file1.txt"
"file10.txt"
"file11.txt"
...
"file98.txt"
"file99.txt"
"file999.txt"
所以现在我们收到了一个包含 101 个文件的列表,其中包含file0.txt
和file999.txt
. 似乎file0.txt
在重命名之前已经枚举了,但最后一个文件还没有,所以file999.txt
也出现在列表中。
结论
显然,在遍历所有(匹配的)项目之前不会枚举整个目录(树)forfiles
。
似乎有一种缓冲区,其中枚举了一些项目,一旦迭代需要更多数据,枚举就会继续下一部分,依此类推,直到到达终点。