如何正确嵌套两个`forfiles`循环(以便内部体扩展两个迭代级别的变量)? [英] How to nest two `forfiles` loops properly (so that the inner body expands variables of both iteration levels)?

查看:662
本文介绍了如何正确嵌套两个`forfiles`循环(以便内部体扩展两个迭代级别的变量)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图嵌套两个 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 outer forfiles command and transfers its replacement to the inner forfiles command;
  • to avoid any trouble with quotes " and cmd /C, quotes within the string after the /C switch of the outer forfiles are avoided by stating their hexadecimal code 0x22;
    (forfiles supports escaping quotes like \", however cmd /C does not care about the \ and so it detects the "; 0x22 has no special meaning to cmd and so it is safe)
  • the if statement checks whether the item enumerated by the outer forfiles loop is a directory and, if not, skips the inner forfiles loop;
  • in case the enumerated sub-directory does not contain any items that match the given pattern, forfiles returns an error message like ERROR: Files of type "*.txt" not found. at STDERR; to avoid such messages, redirection 2> 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆