批处理文件中带有空格和括号的变量

5
我已经阅读了许多有关如何正确处理具有空格、括号和其他特殊字符的变量的 Windows 批处理解析器的不同方法,但没有一项建议似乎能够解决我遇到的问题。
以下是脚本(在尝试任何解决方法之前),其目标是根据找到的 variable01 和 variable02 的值为 variable03 设置一个值:
set variable01="C:\Program Files (x86)\SomeProgram\Subfolder"
set variable02="${macro}"

set variable01=%variable01:"=%
set variable02=%variable02:"=%

set variable03=""

if %variable02:~0,1%==$ (
   if %variable01:~0,1%==$ (
      set variable03=%variable03:"=%
   ) else (
      set variable03=-o '%variable01%'
   )
)

变量variable01和variable02的值在运行脚本之前是未知的 - 它们会被另一个程序替换,因此上面的脚本展示了在替换完成后variable01和variable02的示例值。

当运行此脚本时,我遇到的错误是:

\SomeProgram\Subfolder' was unexpected at this time.

...这对应于上面脚本中的最后一行“set”语句。我认为这个错误是由variable01变量值中的括号引起的。

如果我将该行更改为:

set "variable03=-o '%variable01%'"

...然后我遇到了这个错误:

Files was unexpected at this time.

看起来它试图在variable01中的空格上进行标记化,但解析器仍然不满意。

如果我在脚本顶部添加这一行:

 setlocal enableextensions enableDelayedExpansion

即使我将%variable01%更改为!variable01!,仍然得到相同的错误。

显然,我不明白批处理文件解析器需要满足我的要求,即变量03的值具有以下值:

-o 'C:\Program Files (x86)\SomeProgram\Subfolder'

你有什么建议吗?

2个回答

7
正如Nate所说,这种情况的问题是括号,但整个代码仍然不稳定。
最好使用延迟扩展,因为这样可以防止任何特殊字符的出现。
还应该使用SET的扩展语法set "variable=content" 来用引号括起整个表达式,这样几乎是安全的,并且引号不会添加到内容中。
而且你不需要以后再删除引号。
这应该适用于var1和var2中的任何内容。
setlocal EnableDelayedExpansion
set "variable01=C:\Program Files (x86)\SomeProgram\Subfolder"
set "variable02=${macro}"

set "variable03="

if "!variable02:~0,1!"=="$" (
   if "!variable01:~0,1!"=="$" (
      rem
   ) else (
      set "variable03=-o '!variable01!'"
   )
)
echo(!variable03!

"你应该使用SET的扩展语法,即将整个表达式用引号括起来,然后就几乎安全了...为什么只是几乎安全?在什么情况下会出现失败?" - Samuel
1
@Samuel 当内容本身包含引号时,例如 set "var=She said: "Dog & Cat" to me",它可能是不安全的。但在这种情况下,仅针对路径应该是安全的。 - jeb
"始终最好使用延迟扩展" 当然,当有延迟扩展等效的内容时,这是正确的,但是 %~dp0 的延迟扩展等效会是什么呢? !~dp0 不像一个变量,而 !~dp0! 则为空字符串。(我正在尝试比较 %CD% 与启动批处理文件的目录,以确保它们是相同的。) - Twisted Code
@TwistedCode 但是,%~dp0的扩展是安全的,因为它不包含引号。只有在启用延迟扩展并且路径包含感叹号时才会出现问题。 - jeb

3
问题在于variable01的值中有括号。由于它在if条件中被展开,这些括号被解释为流程控制。修复方法是始终将其放在双引号中。
set variable01="C:\Program Files (x86)\SomeProgram\Subfolder"
set variable02="${macro}"

set variable01=%variable01:"=%
set variable02=%variable02:"=%

set variable03=""

if "%variable02:~0,1%"=="$" (
   if "%variable01:~0,1%"=="$" (
      set variable03=%variable03:"=%
   ) else (
      call :set3 "%variable01%"
   )
)
goto :eof

REM If it is important to have single quotes instead of double then
REM I found I had to call a subroutine.  Otherwise the set could be
REM left up in the else.
:set3
set variable03=-o '%~1'
goto :eof

1
感谢您的回复-这仍然会产生相同的错误,可能是因为variable01仍在那个else子句的上下文中被扩展。我想另一个选择是修改子程序,使其不需要参数,并为它可以采用的每个variable01和variable02组合创建不同的子程序... - Hoobajoob
你确定吗?我用你的问题中的代码会出现错误,但是我用我的代码却没有出现过。我只是将上面的内容复制粘贴到记事本中以确保我没有输错。 - Nate Hekman
请注意 call :set3 行中的 双引号。如果使用单引号,将会出现错误。 - Nate Hekman
是的 - 使用双引号仍然出现相同的错误。我发布的版本是真实版本的简化版本,所以有可能还有其他问题导致您的建议无法正常运行 - 如果我发现有重大差异,我会进行更新。再次感谢! - Hoobajoob
我可以通过在嵌套的if行(if块的第二行)中使用双引号来使其正常工作,即:if“%variable01:〜0,1%”==“$”。我认为一开始的“set”行可能没有问题。 - Hoobajoob
感谢提问者和回答者。我没有意识到即使在echo命令中,我可能也需要在路径使用时加引号。但由于该echo命令位于if结构中,所以右括号被视为块的结束...这太尴尬了,以至于我开始觉得重写我的批处理脚本为Power Shell脚本可能是个好主意... - Twisted Code

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