如何正确嵌套两个“forfiles”循环(以便内部主体扩展两个迭代级别的变量)?
How to nest two `forfiles` loops properly (so that the inner body expands variables of both iteration levels)?
我试图嵌套两个 forfiles
循环,以便内循环的命令从外循环和内循环迭代接收 @
变量。对于后者,需要为外部循环转义 @
变量替换,以便内部 forfiles
命令接收变量名称。
我有一个枚举给定目录的代码片段 (C:\root
),如果迭代项目本身就是一个目录,则列出所有包含的文本文件 (*.txt
) .
但是,它没有按预期工作:我试图用 \
来逃避 @file
的扩展,但它扩展到外循环的值:
2> nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C \"cmd /C echo @relpath -- \@file\""
除了\@file
,还有^@file
、^^@file
、^^^@file
、0x40file
(0x40
是[的十六进制字符编码表示=12=]) 扩展到外部 forfiles
变量的值。
\@file
和 ^^^^@file
也扩展到外部值,分别在 \
和 ^
之前。
连@^file
、@^^file
(见)都不行(后者按字面意思展开为@file
)。
那么:有没有一种方法可以避免从外部 forfiles
循环中替换 @
变量(如 @file
,...),以便内部 forfiles
迭代接收文字变量名并将其扩展为它的值?
我正在开发 Windows 7 64 位。
注:
当当前处理的目录中没有文件与给定掩码 (*.txt
) 匹配时,2> nul
重定向应该避免大量错误消息 ERROR: Files of type "*.txt" not found.
。
这里有一个解决方法 - forfiles 不是一个快速的野兽,所以任何额外的处理都不会很重要。
@echo off
for /f "delims=" %%a in ('2^>nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE echo @path"') do forfiles /P %%a /M *.txt /C "cmd /C echo @relpath -- @file"
pause
技巧是在格式0xHH
中使用forfiles
的十六进制字符代码替换特性,可以自行嵌套。在这种情况下,使用 00x7840
,因此第一个(外部)forfiles
循环将 0x78
部分替换为 x
,导致 0x40
,它在通过将其替换为 @
.
由第二个(内部)forfiles
循环解决
一个简单的 0x40
不 工作,因为 forfiles
在第一遍中替换十六进制代码,然后 然后 它在第二遍中处理 @
变量,因此 0x40file
将首先被 @file
替换,然后通过外部 forfiles
循环扩展到当前迭代的项目。
以下命令行遍历给定的根目录并显示每个直接子目录的相对路径(由外部 forfiles
循环迭代)和其中找到的所有文本文件(由内部 forfiles
循环迭代) =16=] 循环):
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
部分隐藏了外部forfiles
命令的@file
变量名,并将其替换转移到内部forfiles
命令;
- 为避免引号
"
和 cmd /C
出现任何问题,在外部 forfiles
的 /C
开关之后的字符串中的引号通过声明其十六进制代码来避免0x22
;
(forfiles
支持像 \"
这样的转义引号,但是 cmd /C
不关心 \
所以它检测到 "
;0x22
没有对 cmd
有特殊意义,所以它是安全的)
if
语句检查外forfiles
循环枚举的项目是否是一个目录,如果不是,则跳过内forfiles
循环;
- 如果枚举的子目录不包含与给定模式匹配的任何项目,
forfiles
returns STDERR 中的 ERROR: Files of type "*.txt" not found.
错误消息;为避免此类消息,已应用重定向 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
是必需的)。
但是,由于在 forfiles
的 /C
开关之后对命令行参数的错误处理,您实际上需要像 forfiles /C "forfiles forfiles /M *"
一样声明它,因此内部 forfiles
命令加倍。否则会抛出一条错误消息 (ERROR: Invalid argument/option
)。
最佳解决方法已在 post 中找到:forfiles without cmd /c(滚动到底部)。
我试图嵌套两个 forfiles
循环,以便内循环的命令从外循环和内循环迭代接收 @
变量。对于后者,需要为外部循环转义 @
变量替换,以便内部 forfiles
命令接收变量名称。
我有一个枚举给定目录的代码片段 (C:\root
),如果迭代项目本身就是一个目录,则列出所有包含的文本文件 (*.txt
) .
但是,它没有按预期工作:我试图用 \
来逃避 @file
的扩展,但它扩展到外循环的值:
2> nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C \"cmd /C echo @relpath -- \@file\""
除了\@file
,还有^@file
、^^@file
、^^^@file
、0x40file
(0x40
是[的十六进制字符编码表示=12=]) 扩展到外部 forfiles
变量的值。
\@file
和 ^^^^@file
也扩展到外部值,分别在 \
和 ^
之前。
连@^file
、@^^file
(见@file
)。
那么:有没有一种方法可以避免从外部 forfiles
循环中替换 @
变量(如 @file
,...),以便内部 forfiles
迭代接收文字变量名并将其扩展为它的值?
我正在开发 Windows 7 64 位。
注:
当当前处理的目录中没有文件与给定掩码 (*.txt
) 匹配时,2> nul
重定向应该避免大量错误消息 ERROR: Files of type "*.txt" not found.
。
这里有一个解决方法 - forfiles 不是一个快速的野兽,所以任何额外的处理都不会很重要。
@echo off
for /f "delims=" %%a in ('2^>nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE echo @path"') do forfiles /P %%a /M *.txt /C "cmd /C echo @relpath -- @file"
pause
技巧是在格式0xHH
中使用forfiles
的十六进制字符代码替换特性,可以自行嵌套。在这种情况下,使用 00x7840
,因此第一个(外部)forfiles
循环将 0x78
部分替换为 x
,导致 0x40
,它在通过将其替换为 @
.
forfiles
循环解决
一个简单的 0x40
不 工作,因为 forfiles
在第一遍中替换十六进制代码,然后 然后 它在第二遍中处理 @
变量,因此 0x40file
将首先被 @file
替换,然后通过外部 forfiles
循环扩展到当前迭代的项目。
以下命令行遍历给定的根目录并显示每个直接子目录的相对路径(由外部 forfiles
循环迭代)和其中找到的所有文本文件(由内部 forfiles
循环迭代) =16=] 循环):
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
部分隐藏了外部forfiles
命令的@file
变量名,并将其替换转移到内部forfiles
命令; - 为避免引号
"
和cmd /C
出现任何问题,在外部forfiles
的/C
开关之后的字符串中的引号通过声明其十六进制代码来避免0x22
;
(forfiles
支持像\"
这样的转义引号,但是cmd /C
不关心\
所以它检测到"
;0x22
没有对cmd
有特殊意义,所以它是安全的) if
语句检查外forfiles
循环枚举的项目是否是一个目录,如果不是,则跳过内forfiles
循环;- 如果枚举的子目录不包含与给定模式匹配的任何项目,
forfiles
returns STDERR 中的ERROR: Files of type "*.txt" not found.
错误消息;为避免此类消息,已应用重定向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
是必需的)。
但是,由于在 forfiles
的 /C
开关之后对命令行参数的错误处理,您实际上需要像 forfiles /C "forfiles forfiles /M *"
一样声明它,因此内部 forfiles
命令加倍。否则会抛出一条错误消息 (ERROR: Invalid argument/option
)。
最佳解决方法已在 post 中找到:forfiles without cmd /c(滚动到底部)。