【问题标题】:BAT-file: variable contents as part of another variableBAT 文件:变量内容作为另一个变量的一部分
【发布时间】:2012-03-30 19:28:33
【问题描述】:

假设我有变量 a 内容为“123”,变量 b123 中有一些文本。出于某种原因,我想使用变量 a 作为第二个 var 名称的一部分。像这样的:

SET a=123
SET b123=some_text_in_it
rem i want next line to output  "some_text_in_it"
echo %b%a%%

所以我想将文本与 var 内容连接起来,并使用结果字符串作为变量名来回显该内容。上面的示例不起作用,我明白为什么,可能我需要添加某种分组。怎么做,最好是一行?

【问题讨论】:

    标签: variables batch-file


    【解决方案1】:

    这有两种常见的方法
    CALLDelayedExpansion

    setlocal EnableDelayedExpansion
    SET a=123
    SET b123=some_text_in_it
    rem i want next line to output  "some_text_in_it"
    call echo %%b%a%%%
    echo !b%a%!
    

    CALL 变体使用这样一个事实,即调用将第二次重新解析该行,第一次仅扩展 %a%,而双 %% 将减少为单 %
    call echo %b123%
    在第二步中,%b123% 将被扩展。
    但是CALL 技术很慢而且不是很安全,所以应该首选DelayedExpansion。

    DelayedExpansion 的作用是感叹号在比百分比扩展更晚的解析器阶段扩展。
    这也是为什么延迟扩展更安全的原因。

    编辑:包含数字的数组的方法
    如果您正在使用仅包含数字的数组,您也可以使用 set /a 来访问它们。
    这比FORCALL 技术要容易得多,而且它也可以在块中使用。

    setlocal EnableDelayedExpansion
    set arr[1]=17
    set arr[2]=35
    set arr[3]=77
    (
      set idx=2
      set /a val=arr[!idx!]
      echo !var!
    )
    

    【讨论】:

    • @jeb:虽然set /a 对访问数字数组元素很有用,但它不能使用在同一个set /a 命令中修改的索引,原因与您在下面解释我的原因相同。例如:set /a idx+=1, var=arr[!idx!] 没有得到正确的元素,所以这个管理必须在两个分开的set /a 命令中实现。
    【解决方案2】:

    “使用变量作为第二个变量名称的一部分”的最佳理由是通过下标变量访问数组(向量和矩阵)元素。虽然这和上面的例子完全一样,但是看起来更清晰:

    setlocal EnableDelayedExpansion
    set i=123
    set v[123]=Value of element 123
    call echo %%v[%i%]%%
    echo !v[%i%]!
    

    尽管CALLDelayed Expansion 慢,正如jeb 上面所说的,它可以嵌套多次,允许使用第二个(或更深的)间接访问数组元素。此功能可用于复杂的数据结构,如链表。例如:

    set i=123
    set v[123]=456
    set w[456]=Value of element 456
    call call echo %%%%w[%%v[%i%]%%]%%%%
    

    编辑:由于某种我不明白的原因,最后一个命令的延迟扩展版本无法正常工作:

    call echo !w[%%v[%i%]%%]!
    

    【讨论】:

    • call echo !w[%%v[%i%]%%]! 将按 标准 顺序展开。 1. 扩展至call echo !w[%v[123]%]!,2. 延迟扩展至call echo ,因为w[%v[123]%] 不是定义变量,3. 调用将被执行并开始一个新的百分比扩展。您的示例应该类似于call echo %%w[!v[%i%]!]%% Btw。在 CALL 之后,将永远不会执行延迟扩展,在 SO:How does CMD.EXE parse scripts 进行了解释
    • @Aacini:好吧,我有类似的理由使用这个功能。有一些以多种语言本地化的字符串,其名称除后缀外都相同(例如,'my_text_1_de'、'my_text_1_it'...)。所以我的目标是在用户选择语言后为变量分配适当的 i18n 字符串。
    • @jeb:当然!:延迟扩展是在执行命令之前完成的,在这种情况下(call),它负责第二次扩展百分号。非常感谢:)
    • @AleksandrKravets:我可以建议您像其他语言的标准数组一样将变量名称的变化部分括在括号中吗? 'my_text_1[de]','my_text_1[it]' 虽然这对于 Batch 处理是一样的,但是对于程序员来说更清楚!
    • @Aacini:对不起,但您提出的解决方案是一种解决方法,而不是您可以在 Python 或 Perl 或任何普通编程语言中找到的数组支持。你甚至可以把它想象成一个哈希表(索引是字符串,记住)。但它不是真正的哈希表,它是 Q&D 解决方案,有用、直观,但仍然是一种解决方法。也许我在吹毛求疵,但那是因为我不是一个真正的 BAT 爱好者,我只需要使用 Bat-file。
    【解决方案3】:

    如果两个变量都被分配在扩展它们的同一个带括号的块中,那么还有另一种方法可以完成这项任务,这种方法非常有用。该解决方案类似于 jeb 的延迟扩展答案,不同之处在于它使用 FOR 变量而不是正常扩展。

    @echo off
    setlocal enableDelayedExpansion
    
    :: Define a linefeed value - only needed for last variant
    set LF=^
    
    
    :: The two blank lines above are critical to the definition of LF
    
    (
      set A=123
      set B123=some_text_in_it
    
      rem ECHO !B%A%! will fail because A was assigned within the same block
    
      rem This works as long as A does not contain * or ?
      for %%A in ("!A!") do echo !B%%~A!
    
      rem This works as long as A does not start with EOL character (; by default)
      for /f "delims=" %%A in ("!A!") do echo !B%%A!
    
      rem This will never fail because it disables EOL by setting it to a linefeed
      for /f eol^=^%LF%%LF%^ delims^= %%A in ("!A!") do echo !B%%A!
    )
    

    也可以使用 CALL 技巧,但相对来说它非常慢,而且不可靠 - 请参阅 CALL me, or better avoid call

    Explain how dos-batch newline variable hack works 讨论了换行变量的定义和使用。

    HOW TO: FOR /F Disabling EOL or using a quote as delim 详细介绍了将 EOL 设置为换行符。使用 LF 变量而不是在 FOR 语句中嵌入换行符只是该技术的扩展。它使代码更具可读性。

    编辑
    对任何有效值安全使用 FOR 技术的更简单方法是将 EOL 设置为等号,因为任何用户定义的变量都不能在名称中包含 =。

    for /f "eol== delims=" %%A in ("!A!") do echo !B%%A!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-08
      • 2019-05-08
      • 2015-11-04
      • 1970-01-01
      • 2022-10-05
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      相关资源
      最近更新 更多