如何在批处理文件中的if语句中转义带括号的变量?

25

运行此批处理文件

@echo off
set a=some value with (parentheses) inside
if 1 == 1 (
    set PATH=%a%
)

出现错误:内部在此时是意外的

如何转义变量a以避免此错误?

4个回答

32

您可以使用两种不同的方法

使用扩展语法的 set 命令,带引号的 set "var=content" 会将 var 设置为 content, 引号包裹 content ,所以特殊字符不会产生问题,并且它使用引号之前的内容作为 content(不包括引号本身)。

@echo off
set a=some value with (parentheses) inside
if 1 == 1 (
    set "PATH=%a%"
)

使用延迟扩展(如shf301的答案所示),同时将值传递到主范围。

@echo off
setlocal enabledelayedexpansion
set a=some value with (parentheses) inside
if 1 == 1 (
    set "localScope_PATH=!a!"
    rem now transfer it to the global scope
    FOR /F "delims=" %%A in ("!localScope_PATH!") DO (
       endlocal
       set "path=%%A"
    )
)
在这种情况下,扩展的集合语法并不是必需的,我只是使用它来避免在行末隐藏空格。
编辑:我可以将其与setlocal EnableDelayedExpansion结合使用,并使用!代替%来惰性评估变量的值吗?当我尝试时,我得到了“)!在此时意外”的错误。
你可以这样做,但这是逆生产的。
@echo off
Setlocal EnableDelayedExpansion
set a=some value with (parentheses) inside
if 1 == 1 (
    set PATH=!a:^)=^^^)!
    set path
)

如果你的路径中包含在 ) 前面的脱字符(^),像这样:C:\programs (x86^)

为了理解扩展是如何工作的,你可以阅读SO:How does the Windows Command Interpreter (CMD.EXE) parse scripts?

编辑2:路径中有引号时会出现更多问题
根据这个问题,当路径中包含引号时可能会发生另一个括号的问题。

示例
path="C:\Program Files (x86)";C:\Program Files (x86)\Skype

虽然不必在此处使用引号,但是这是允许的,但这会破坏扩展的SET语法,因为现在set "newPath=%path%"就会扩展为

set "newPath="C:\Program Files (x86)";C:\Program Files (x86)\Skype"

现在至少有一个括号不在引号内,可以打破命令块。

但是你可以从路径变量中简单地删除所有引号,因为引号在这里并不必要。

set "newPath=%path:"=%"

"set "var=content"" - 谁能想到呢? :) - Piotr Dobrogost
在这种情况下,扩展的集合语法不是必需的。您能解释一下吗? - Piotr Dobrogost
我更喜欢这种方式,因为它避免了行末隐藏空格带来的问题。 - jeb
尝试解释No2:这并不是必要的,因为延迟扩展对特殊字符是安全的。我在这里使用它只是为了展示你需要多少个脱字符。 - jeb
我花了几个小时尝试在变量中将 ")" 替换为 "^)",直到找到你的帮助才发现只需使用 set "var=content" 而不需要任何替换。谢谢! - meir
显示剩余3条评论

5
< p > 这里的问题在于 %a% 中的 )。您可以进行一些替换以转义 )

@echo off
set a=some value with (parentheses) inside
if 1 == 1 (
    set PATH=%a:)=^)%
)

我能否将此与 setlocal EnableDelayedExpansion 结合使用,并使用 ! 而不是 % 来延迟计算变量的值?当我尝试时,出现了 )! was unexpected at this time. 错误。 - Piotr Dobrogost
你可以将它们组合起来,但这并不是必须的,因为延迟扩展对任何特殊字符都是安全的,包括 ) - jeb
我会使用延迟扩展或者我的方法,但不会两者同时用。仅代表个人意见。 - Patrick Seymour

3
使用延迟扩展可以解决这个问题:
@echo off
setlocal enabledelayedexpansion
set a=some value with (parentheses) inside
if 1 == 1 (
    set PATH=!a!
)

如果没有延迟扩展,if块(从if到结尾的))会先替换%a%,然后解析并运行该块。使用延迟扩展后,在块解析后不会扩展!a!。因此,解析逻辑不会看到a中的)并且不会引起问题。


你修改了本地的 PATH 变量。你打算如何将这个值传递到全局范围? - Piotr Dobrogost
在我的答案中有一个示例,可以将变量传递到全局范围。 - jeb

3

括号和变量总是难以混合使用。使用子程序代替。

@Echo Off
Set a=some value with (parentheses) inside
If 1 == 1 Call :SetPath
Echo %Path%
Exit /B

:SetPath
Set "Path=%a%"
SetX "Path" "%a%"
Exit /B

我设置了变量两次,一次使用Set用于当前shell会话,另一次使用SetX将其设置为系统范围内的未来shell会话。如果不需要,请删除其中一个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接