诀窍是使用forfiles
format 中的十六进制字符代码替换功能0xHH
,可以自行嵌套。在这种情况下,00x7840
使用 ,因此第一个(外部)forfiles
循环将0x78
部分替换为x
,从而导致0x40
,这又由第二个(内部)forfiles
循环通过将其替换为 来解决@
。
一个简单的0x40
不起作用,因为它在第一遍中forfiles
替换十六进制代码,然后在第二遍中处理@
变量,因此0x40file
将由@file
first 替换,然后由外forfiles
循环扩展为当前迭代的项目。
以下命令行遍历给定的根目录并显示每个直接子目录的相对路径(由外forfiles
循环迭代)和其中找到的所有文本文件(由内forfiles
循环迭代):
2> nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C 0x22cmd /C echo @relpath -- 00x7840file0x22"
输出可能看起来像(左侧的相对子目录路径,右侧的文本文件):
.\data_dir -- "text_msg.txt"
.\logs_dir -- "log_book.txt"
.\logs_dir -- "log_file.txt"
代码说明:
- 如上所述,该
00x7840file
部分对外部命令隐藏@file
变量名forfiles
并将其替换转移到内部forfiles
命令;
- 为避免引号
"
和的任何麻烦,通过说明它们的十六进制代码来避免外部切换cmd /C
后字符串中的引号;
(支持转义引号,但不关心,所以它检测到;没有特殊含义,所以它是安全的)/C
forfiles
0x22
forfiles
\"
cmd /C
\
"
0x22
cmd
- 该
if
语句检查外forfiles
循环枚举的项目是否为目录,如果不是,则跳过内forfiles
循环;
- 如果枚举的子目录不包含任何与给定模式匹配的项目,则
forfiles
返回一条错误消息ERROR: Files of type "*.txt" not found.
,如 STDERR;为避免此类消息,2> nul
已应用重定向;
逐步更换:
这是上面的命令行,但删除了重定向,仅用于演示:
forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C 0x22cmd /C echo @relpath -- 00x7840file0x22"
我们现在将提取将一个接一个执行的嵌套命令行。
取上述示例输出的第一行(.\data_dir -- "text_msg.txt"
)的项目,外部forfiles
命令执行的命令行为:
cmd /C if TRUE==TRUE forfiles /P "C:\root" /M *.txt /C "cmd /C echo ".\data_dir" -- 0x40file"
所以内部forfiles
命令行看起来像(cmd /C
删除,并且if
满足条件):
forfiles /P "C:\root" /M *.txt /C "cmd /C echo ".\data_dir" -- 0x40file"
现在内部forfiles
命令执行的命令行是(注意周围删除的文字引号和变量的值.\data_dir
的即时替换):0x40file
@file
cmd /C echo .\data_dir -- "text_msg.txt"
像这样从最内层到最外层的命令行遍历这些步骤,您甚至可以嵌套两个以上的forfiles
循环。
笔记:
所有与路径或文件名相关的@
变量均由带引号的字符串替换;但是,上面显示的示例输出不包含目录路径的任何引号;这是因为在切换后从字符串forfiles
中删除任何文字(非转义)引号;要将它们返回到此处的输出中,请在命令行中替换为; 也可以(但不推荐,但不要混淆)。"
/C
@relpath
00x7822@relpath00x7822
\\\"@relpath\\\"
cmd
附录:
由于forfiles
不是内部命令,因此应该可以在没有cmd /C
前缀的情况下嵌套它,例如forfiles /C "forfiles /M *"
,例如(除非使用任何额外的内部或外部命令、命令连接、重定向或管道,其中cmd /C
是强制性的)。
但是,由于/C
切换后命令行参数的错误处理forfiles
,实际上需要将其声明为forfiles /C "forfiles forfiles /M *"
,因此内部forfiles
命令加倍。否则会抛出错误消息 ( ERROR: Invalid argument/option
)。
在这篇文章中找到了这个最好的解决方法:forfiles without cmd /c(滚动到底部)。