我进行了一些使用forfiles
的测试--以下是结果...
目的与范围
这里的测试案例旨在证明forfiles
是否在遍历所有(子)项目之前完成了对给定目录(树)的枚举。
下面的列表显示了这些测试所涵盖的模式:
- 文件模式(
/M
)总是*.txt
;
- 文件模式(
/M
)仅匹配文件,而不是目录;
- 始终有一个根搜索路径(
/P
);
- 仅使用内部
cmd.exe
命令(以cmd / C
为前缀)在主体(/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
我的意图是等待文件夹
sub3
中的项目
file2.txt
被迭代,然后执行以下任务:
- 在当前迭代的
sub3
中,
- 删除已经迭代的
file1.txt
;
- 删除尚未迭代的
file3.txt
;
- 创建尚未迭代的
file4.txt
;
- 删除已经迭代的容器
sub1
;
- 删除尚未迭代的容器
sub4
;
- 通过将
file2.txt
重命名为file4.txt
来更改sub2
(已经迭代)的内容;
- 通过将
file2.txt
重命名为file4.txt
来更改sub5
(尚未迭代)的内容;
- 创建尚未迭代的容器
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
内容的修改。在迭代
sub3
期间创建的
sub6
根本没有被识别。
forfiles
,非递归
对于没有/S
选项的forfiles
,使用平面目录树:
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
在这个实验中,我们在迭代到
file1.txt
时,立即将
file99.txt
重命名为
file999.txt
。
执行的命令行如下:
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个文件。
在这个实验中,当迭代到
file1.txt
时,我们将
file99.txt
重命名为
file100.txt
。
执行的命令行是:
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
,一旦迭代到
file1.txt
。
要执行的命令行如下:
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
在迭代所有(匹配的)项之前没有枚举整个目录(树)。似乎有一种缓冲区,可以将某些项枚举出来,并且只要迭代需要更多数据,枚举就会继续下一个部分,依此类推,直到结束。