如何正确嵌套两个`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>如果@ isdir == TRUE forfiles / P @path / M * .txt / C \cmd / C echo @relpath - nul forfiles / PC:\root/ M - \ @ file\
除了 \ @ file
, ^ @ file
, ^^ @ file
, ^^ @ file
, 0x40file
( 0x40
是十六进制字符代码表示< c $ c> @ )展开为外部 forfiles
变量的值。
\\\ @ file
和 ^^^^ @ file
展开为外部值, \
和
(请参阅这则讯息)无法使用(后来扩展到 ^
。
甚至 @ ^ file
code> @ ^^档案 @file
字面上)。
所以:有一种方法来替换<$ c外部 forfiles
的$ c> @ 变量(如 @file
,...) >循环,以便内部 forfiles
迭代接收文字变量名称并将其扩展为其值?
在Windows 7 64位上运行。
注意:
2> nul
重定向应避免许多错误消息 ERROR:未找到类型* .txt的文件
当当前处理的目录中没有文件匹配给定掩码( *。txt
)。
请以格式 一个简单的 以下命令行遍历给定的根目录,并显示每个直接子目录的相对路径(由外部 输出可能看起来像(相对子目录路径左,文本文件右): 0xHH
使用可以嵌套的 forfiles
的十六进制字符代码替换功能部件。在这种情况下,使用 00x7840
,因此第一个(外部) forfiles
由 x
产生的 0x78 部分,导致
0x40
($)
0x40
不会 工作为 forfiles
替换第一次通过的十六进制代码然后 在第二遍中处理 @
变量,因此 0x40file
@file
,然后通过外部 forfiles
循环扩展到当前迭代的项目。
forfiles
loop)和其中找到的所有文本文件(由 forfiles
循环迭代):
2>如果@ isdir == TRUE forfiles / P @path / M * .txt / C 0x22cmd / C echo @relpath - 00x7840file0x22/ Ccmd / C
.\data_dir - text_msg.txt
。 logs_dir - log_book.txt
.\logs_dir - log_file.txt
代码说明:
- 如上所述,
00x7840file
部分隐藏了@file
变量名从外部forfiles
命令并将其替换传递到内部forfiles
命令; -
/ C $>之后的字符串中的引号引用
和
cmd / C
通过说明其十六进制代码0x22
;
$ b $避免外部forfiles
的c $ c> b(forfiles
支持转义引号,例如\
,但cmd / C
不关心\
,因此它检测到;
0x22
对cmd
没有特殊意义,因此是安全的) -
if
语句检查外部forfiles
循环中枚举的项是否是目录,如果不是,forfiles
循环; - 如果枚举子目录不包含与给定模式匹配的任何项目,
forfiles
返回一个错误信息,例如ERROR:在STDERR找不到类型为* .txt的文件
为了避免这样的消息,重定向2> nul
已套用;
逐步替换:
以下是上述命令行,但重定向已删除,仅用于演示:
forfiles / PC:\root/ M*/ Ccmd / 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 / PC:\root/ M * .txt / Ccmd / C echo.\data_dir - 0x40file
所以内部 forfiles
命令行看起来像( cmd / C
已删除,并且如果满足
条件):
forfiles / PC:\root/ M * .txt / Ccmd / C echo.\data_dir - 0x40file
现在命令行由 forfiles
命令是(注意删除的字面引用 .\data_dir
,并立即替换 0x40file
变量 @file
):
cmd / C echo .\data_dir - text_msg.txt
通过这些步骤从最内层到最外层命令行,您可以嵌套两个以上的 forfiles
循环。
注意:
所有与路径或文件名相关的 @
替换为引用字符串;但是,上面显示的示例输出不包含目录路径的任何周围引号;这是因为 forfiles
从 > / C switch;要在输出中返回它们,请在命令行中将
@relpath
替换为 00x7822 @ relpath00x7822
; \\\@ relpath\\\
code> cmd )。
b
由于 forfiles
不是内部命令,因此应该可以嵌套它而不用 cmd / C $例如(除非使用任何附加的内部或外部命令,命令连接,重定向或管道,例如
forfiles / Cforfiles / M *
其中 cmd / C
是必需的)。
然而,由于在 / C
切换
forfiles
,你实际上需要像 forfiles / Cforfiles forfiles / M *
,因此内部 forfiles
命令翻了一番。否则将抛出一个错误消息( ERROR:无效的参数/选项
)。
此最佳解决方法已在此帖中找到:无cmd / c的文件(滚动到底部)。
I am trying to nest two forfiles
loops so that the command of the inner loop receives @
variables from both the outer and the inner loop iteration. For the latter the @
variable replacement needs to be escaped for the outer loop so that the inner forfiles
command receives the variable name.
I have got a code snippet that enumerates a given directory (C:\root
), and if the iterated item is a directory on its own, all the contained text files (*.txt
) are listed.
However, it does not work as expected: I tried to escape the expansion of @file
with \
, but it expands to the value of the outer loop:
2> nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C \"cmd /C echo @relpath -- \@file\""
Besides \@file
, also ^@file
, ^^@file
, ^^^@file
, 0x40file
(0x40
being the hexadecimal character code representation of @
) expand to the value of the outer forfiles
variable.
\\@file
and ^^^^@file
expand to the outer value too, with \
and ^
preceeded, respectively.
Even @^file
, @^^file
(see this post) does not work (the latter expands to @file
literally).
So: is there a way to escape the replacement of @
variables (like @file
,...) from the outer forfiles
loop so that the inner forfiles
iteration receives the literal variable name and expands it to its value?
I am working on Windows 7 64-bit.
Note:
The 2> nul
redirection should avoid numerous error messages ERROR: Files of type "*.txt" not found.
when no file in the currently processed directory matches the given mask (*.txt
).
The trick is to use the hexadecimal character code replacement feature of forfiles
in the format 0xHH
, which can be nested on its own. In this context, 00x7840
is used, hence the first (outer) forfiles
loop replaces the 0x78
portion by x
, resulting in 0x40
, which is in turn resolved by the second (inner) forfiles
loop by replacing it with @
.
A simple 0x40
does not work as forfiles
replaces hexadecimal codes in a first pass and then it handles the @
variables in a second pass, so 0x40file
will be replaced by @file
first and then expanded to the currently iterated item by the outer forfiles
loop both.
The following command line walks through a given root directory and displays the relative path of each immediate sub-directory (iterated by the outer forfiles
loop) and all text files found therein (iterated by the inner forfiles
loop):
2> nul forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C 0x22cmd /C echo @relpath -- 00x7840file0x22"
The output might look like (relative sub-directory paths left, text files right):
.\data_dir -- "text_msg.txt" .\logs_dir -- "log_book.txt" .\logs_dir -- "log_file.txt"
Explanation of Code:
- as described above, the
00x7840file
portion hides the@file
variable name from the outerforfiles
command and transfers its replacement to the innerforfiles
command; - to avoid any trouble with quotes
"
andcmd /C
, quotes within the string after the/C
switch of the outerforfiles
are avoided by stating their hexadecimal code0x22
;
(forfiles
supports escaping quotes like\"
, howevercmd /C
does not care about the\
and so it detects the"
;0x22
has no special meaning tocmd
and so it is safe) - the
if
statement checks whether the item enumerated by the outerforfiles
loop is a directory and, if not, skips the innerforfiles
loop; - in case the enumerated sub-directory does not contain any items that match the given pattern,
forfiles
returns an error message likeERROR: Files of type "*.txt" not found.
at STDERR; to avoid such messages, redirection2> nul
has been applied;
Step-by-Step Replacement:
Here is the above command line again but with the redirection removed, just for demonstration:
forfiles /P "C:\root" /M "*" /C "cmd /C if @isdir==TRUE forfiles /P @path /M *.txt /C 0x22cmd /C echo @relpath -- 00x7840file0x22"
We will now extract the nested command lines which are going to be executed one after another.
Taking the items of the first line of the above sample output (.\data_dir -- "text_msg.txt"
), the command line executed by the outer forfiles
command is:
cmd /C if TRUE==TRUE forfiles /P "C:\root" /M *.txt /C "cmd /C echo ".\data_dir" -- 0x40file"
So the inner forfiles
command line looks like (cmd /C
removed, and the if
condition is fulfilled):
forfiles /P "C:\root" /M *.txt /C "cmd /C echo ".\data_dir" -- 0x40file"
Now the command line executed by the inner forfiles
command is (notice the removed literal quotes around .\data_dir
and the instant replacement of 0x40file
by the value of variable @file
):
cmd /C echo .\data_dir -- "text_msg.txt"
Walking though these steps from the innermost to the outermost command line like that, you could nest even more than two forfiles
loops.
Note:
All path- or file-name-related @
-variables are replaced by quoted strings each; however, the above shown sample output does not contain any surrounding quotes for the directory paths; this is because forfiles
removes any literal (non-escaped) quotes "
from the string after the /C
switch; to get them back in the output here, replace @relpath
in the command line by 00x7822@relpath00x7822
; \\\"@relpath\\\"
works too (but is not recommended though to not confuse cmd
).
Appendix:
Since forfiles
is not an internal command, it should be possible to nest it without the cmd /C
prefix, like forfiles /C "forfiles /M *"
, for instance (unless any additional internal or external command, command concatenation, redirection or piping is used, where cmd /C
is mandatory).
However, due to erroneous handling of command line arguments after the /C
switch of forfiles
, you actually need to state it like forfiles /C "forfiles forfiles /M *"
, so the inner forfiles
command doubled. Otherwise an error message (ERROR: Invalid argument/option
) is thrown.
This best work-around has been found at this post: forfiles without cmd /c (scroll to the bottom).
这篇关于如何正确嵌套两个`forfiles`循环(以便内部体扩展两个迭代级别的变量)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!