Windows 批处理编程:间接/嵌套变量评估

发布于 2025-01-07 07:01:23 字数 542 浏览 0 评论 0 原文

我们有一个列出一堆路径的文本文件,以及一个从该文件读取行的批处理文件。

例如,TargetFolders.txt 可能包含以下行:

%ProgramFiles%\Acme\FooBar %VersionNumber%

当然,当我们从文本文件中读取此行(使用 FOR 命令)时,变量 %%I 会接收实际的行文本,并带有 % 符号,而不是替换变量值。那么,

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    echo Folder: %%I
)

打印

Folder: %ProgramFiles%\Acme\FooBar %VersionNumber%

如何让它替换实际的变量值,以便打印

Folder: C:\Program Files\Acme\FooBar 7.0

We have a text file that lists a bunch of paths, and a batch file that reads the lines from this file.

For instance, TargetFolders.txt might contain the line:

%ProgramFiles%\Acme\FooBar %VersionNumber%

Naturally, when we read this line from the text file (using a FOR command), the variable %%I receives the actual line text, with the % signs rather than replacing the variable values. So,

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    echo Folder: %%I
)

Prints

Folder: %ProgramFiles%\Acme\FooBar %VersionNumber%

How does one make it replace the actual variable values, so that it prints

Folder: C:\Program Files\Acme\FooBar 7.0

?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

拥抱影子 2025-01-14 07:01:23
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    for /F "usebackq delims=" %%J in (`echo %%I`) do echo Folder: %%J
)

就这样吧。 (这就是你想要的,对吧?)

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    for /F "usebackq delims=" %%J in (`echo %%I`) do echo Folder: %%J
)

There you go. (This is what you wanted, right?)

不打扰别人 2025-01-14 07:01:23

只需添加 CALL 即可解决您的问题。 (而且您对 VersionNumber 的定义也是错误的)

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  call echo Folder: %%I
)

但是,如果您的文件包含未加引号的特殊字符,例如 &><,则此操作将会失败,<代码>|

例如,以下行将失败:

%ProgramFiles%\This&That\ %VersionNumber%

如果它被引用,它将起作用

"%ProgramFiles%\This&That\" %VersionNumber%

CALL 还将破坏任何带引号的插入符号: "^" 将变为 "^^"

最好解决方案是修改您的文本文件并将每个 % 替换为 !

!ProgramFiles!\Acme\FooBar !VersionNumber!
!ProgramFiles!\This&That !VersionNumber!

现在您可以安全地使用延迟扩展来扩展循环内的变量。

setlocal enableDelayedExpansion
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)

如果您的文本文件已经包含要保留的 !,则必须对其进行转义。另外,如果 ^ 出现在带有 ! 的行上,则必须对其进行转义。

preserve caret ^^ and exclamation ^! by escaping
caret ^ without exclamation is no problem

或者,您可以用变量替换脱字符号和感叹号文字

alternate method to preserve caret !c! and exclamation !x!
caret ^ without exclamation still no problem

,然后在批处理中定义变量

setlocal enableDelayedExpansion
set "x=^!"
set "c=^"
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)

Simply adding a CALL may solve your problem. (also your definition of VersionNumber was wrong)

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  call echo Folder: %%I
)

But this will fail if your file contains unquoted special characters like &, >, <, |.

For example, the following line would fail:

%ProgramFiles%\This&That\ %VersionNumber%

It will work if it is quoted

"%ProgramFiles%\This&That\" %VersionNumber%

The CALL will also mangle any quoted carets: "^" will become "^^"

The best solution would be to modify your text file and replace each % with !.

!ProgramFiles!\Acme\FooBar !VersionNumber!
!ProgramFiles!\This&That !VersionNumber!

Now you can safely use delayed expansion to expand the variables within the loop.

setlocal enableDelayedExpansion
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)

If your text file already has ! that you want to preserve, then it must be escaped. Also ^ will have to be escaped if it appears on a line with a !.

preserve caret ^^ and exclamation ^! by escaping
caret ^ without exclamation is no problem

Alternatively you can substitute variables for caret and exclamation literals

alternate method to preserve caret !c! and exclamation !x!
caret ^ without exclamation still no problem

And then define the variables in your batch

setlocal enableDelayedExpansion
set "x=^!"
set "c=^"
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)
呆头 2025-01-14 07:01:23

为了用更强大的变体补充现有的有用答案,该变体还正确处理以下嵌入字符:& | < > ^[1]

请注意,为了简单起见,我使用字符串来驱动外部循环,其文字值 - 稍后将通过内部循环 - 是 %ProgramFiles%\Acme\FooBar %VersionNumber%,因为 % 实例加倍。

set "VersionNumber=0.7"
for /f "delims=" %%f in ("%%ProgramFiles%%\Acme\FooBar %%VersionNumber%%") do (
  for /f "delims=" %%g in ('echo "%%f"') do echo Folder: [%%~g]
)

这会产生类似于 Folder 的内容: [C:\程序Files\Acme\FooBar 0.7]

注意:

  • 默认情况下,for /f 解释一个单引号字符串('...') 作为要执行的命令并捕获其输出; delims= 告诉 for 将输出作为一个整体捕获到单个变量中。
    (虽然您可以usebackq`...` 封闭的命令一起使用,但在这种情况下这样做没有任何优势。)
  • 请注意对外部循环变量的引用是如何用双引号引起来的("%%f"),这使得值可以包含所述特殊字符而无需破坏 echo 命令。

  • 因为输出也将被双引号括起来,所以 ~ 运算符用于在回显时从捕获的值中去除封闭的双引号(%%~g)。


[1] 另外处理嵌入式 " 实例的解决方案比较棘手:

  rem Construct a variable whose value contains all shell metacharacters.
  rem % chars. meant to be treated as literals must be doubled (%%)
  rem Note the extra " at the end, which appends an unbalanced double quote. 
set "var=%%OS%% & %%ba^r | (baz) <mo'>""

  rem Embedded " chars. must be escaped as "" (doubling them).  
for /f "delims=" %%v in ('echo "%var:"=""%"') do set "expandedVar=%%~v"

  rem Note: The resulting variable's value: 
  rem  - can only be echoed *double-quoted*, as the command will otherwise break.
  rem  - still contains the *doubled* embedded double quotes, which, however,
  rem    is the escaping that other Microsoft programs expect.
echo "[%expandedVar%]"

这会产生:"[Windows_NT & %ba^r | (baz) ""]"

To complement the existing helpful answers with a more robust variant that also handles the following embedded characters correctly: & | < > ^[1]
:

Note that for simplicity I'm using a string to drive the outer loop, whose literal value - to be expanded later by the inner loop - is %ProgramFiles%\Acme\FooBar %VersionNumber%, due to the doubled % instances.

set "VersionNumber=0.7"
for /f "delims=" %%f in ("%%ProgramFiles%%\Acme\FooBar %%VersionNumber%%") do (
  for /f "delims=" %%g in ('echo "%%f"') do echo Folder: [%%~g]
)

This yields something like Folder: [C:\Program Files\Acme\FooBar 0.7]

Note:

  • By default, for /f interprets a single-quoted string ('...') as a command to execute and whose output to capture; delims= tells for to capture the output as a whole in a single variable.
    (While you can use usebackq with a `...`-enclosed command instead, there's no advantage to doing so in this case.)
  • Note how the reference to the outer loop's variable is double-quoted ("%%f"), which is what allows the value to contain said special characters without breaking the echo command.

  • Because the output will then also be double-quoted, the ~ operator is used to strip the enclosing double quotes from the captured value on echoing it (%%~g).


[1] A solution that additionally handles embedded " instances is trickier:

  rem Construct a variable whose value contains all shell metacharacters.
  rem % chars. meant to be treated as literals must be doubled (%%)
  rem Note the extra " at the end, which appends an unbalanced double quote. 
set "var=%%OS%% & %%ba^r | (baz) <mo'>""

  rem Embedded " chars. must be escaped as "" (doubling them).  
for /f "delims=" %%v in ('echo "%var:"=""%"') do set "expandedVar=%%~v"

  rem Note: The resulting variable's value: 
  rem  - can only be echoed *double-quoted*, as the command will otherwise break.
  rem  - still contains the *doubled* embedded double quotes, which, however,
  rem    is the escaping that other Microsoft programs expect.
echo "[%expandedVar%]"

This yields: "[Windows_NT & %ba^r | (baz) <mo'>""]"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文