批处理文件 - 如何在使用FindStr时使用百分号

3

我有一个批处理文件,其头部内容如下:

@Echo off
SETLOCAL EnableDelayedExpansion EnableExtensions

在一个带有括号()If语句中,我有以下代码:
Echo SOME_VAR|FindStr /r /C:"^.*SOME.*$"
Echo Error: !errorlevel!
Echo %%SOME_VAR%%|FindStr /r /C:"^.*SOME.*$"
Echo Error: !errorlevel!

这将会打印出以下内容:
SOME_VAR
Error: 0
Error: 1

如果SOME_VAR是现有的环境变量,那么就会得到以下结果。如果删除该变量,则可以得到预期的成功。
发生了什么?我需要更多地转义吗?如果变量存在,如何在第二个结果上获得成功查找?我只想进行文本搜索,其中搜索文本包含百分号(%)字符,并恰好与现有变量名匹配。
顺便说一下,进行比较的源最终也将成为一个变量,在其中我已从Windows注册表中加载了PATH。 因此,我要搜索的字符串将变成/C:"^.%%SOME_VAR%%;.*$" 我的PATH变量看起来像这样:
%SOME_VAR%;C:\Windows\system32...................etc
1个回答

3
是的,还需要另一层转义,因为管道的每一侧都是在命令行环境下通过CMD /C执行的,而不是批处理环境。初始批处理解析器将%%SOME_VAR%%转换为%SOME_VAR%。然后,如果变量未定义,命令行解析器将保留%SOME_VAR%不变。我们需要防止变量已定义时的扩展。在命令行环境中,加倍百分号无效。解决方案是在百分号之间插入一个消失的脱字符,如%^SOME_VAR%。脱字符被视为变量名的一部分,因此它可以防止变量扩展(除非您有一个名为^SOME_VAR的变量)。扩展失败后,脱字符会被正常的转义过程消耗。必须转义脱字符,以便批处理解析器将脱字符传递给CMD /C命令行环境。
最终批处理命令行如下:
Echo %%^^SOME_VAR%% | FindStr SOME_VAR

注意:我将FINDSTR命令简化为更简单但等效的搜索。
当您修改右侧的搜索以包括百分号时,您还需要插入转义符号。
针对评论中的问题更新:
以下代码演示了一些可能使用变量的方法:
@echo off
setlocal disableDelayedExpansion

:: Put both the text you want to search, as well as the search itself, in variables
set "text=%%SOME_VAR%%;C:\Windows\system32...................etc"
set "search=%%SOME_VAR%%"
echo text=%text%
echo search=%search%
echo(

echo(
echo Starting with delayed expansion disabled
echo -----------------------------------------
echo(

:: Use delayed expansion to safely expand any variable without worrying about content.
:: Both sides of the pipe are executed in a command line context with delayed expansion disabled.
:: You must use CMD /V:ON to enable delayed expansion within the pipe.

echo Test when SOME_VAR is undefined:
set "SOME_VAR="
if 1==1 (
  cmd /v:on /c echo !text!|cmd /v:on /c findstr "!search!"
)
echo(

echo Test when SOME_VAR is defined:
set "SOME_VAR=DEFINED"
if 1==1 (
  cmd /v:on /c echo !text!|cmd /v:on /c findstr "!search!"
)
echo(


setlocal enableDelayedExpansion
echo(
echo(
echo Now try with delayed expansion enabled
echo -----------------------------------------
echo(

:: Even though delayed expansion is enabled within the batch script, it is still disabled
:: within the pipe, so we still have to explicitly enable it via CMD /V:ON.
:: But now we must prevent expansion of the variables while in the batch context.
:: You have two options:

:: 1) Escape the !. The escape syntax changes depending on whether it is inside quotes or not:
echo Escape test:
if 1==1 (
  cmd /v:on /c echo ^^!text^^!|cmd /v:on /c findstr "^!search^!"
)
echo(

:: 2) Enclose both sides of the pipe within parentheses (very weird, but it works)
echo Parentheses test:
if 1==1 (
  (cmd /v:on /c echo !text!)|(cmd /v:on /c findstr "!search!")
)

--OUTPUT--

text=%SOME_VAR%;C:\Windows\system32...................etc
search=%SOME_VAR%


Starting with delayed expansion disabled
-----------------------------------------

Test when SOME_VAR is undefined:
%SOME_VAR%;C:\Windows\system32...................etc

Test when SOME_VAR is defined:
%SOME_VAR%;C:\Windows\system32...................etc



Now try with delayed expansion enabled
-----------------------------------------

Escape test:
%SOME_VAR%;C:\Windows\system32...................etc

Parentheses test:
%SOME_VAR%;C:\Windows\system32...................etc

谢谢,这是一个非常清晰的解释,而且它有效...但是当我回显一个变量时,比如 %SOME_VAR%; 这样的值,我该怎么做呢?我无法构建我要搜索的字符串并在其中添加 carets。 - jeremfg
这是一个很好的答案,但我看不出在这里使用cmd /v:on的原因。为什么不直接使用echo %%text%% | findstr "%%search%%"呢?因为似乎这只涉及到带有百分号的普通变量名。然后百分号扩展也是安全的。 - jeb
@jeb - 哎呀!你关于右侧的问题是正确的。我不必要地复杂化了搜索位。但左侧处理注册表 PATH 值,它可能具有在正常扩展中不安全的内容。 - dbenham
@dbenham - 是的,我在上面的评论中没有在右侧使用/需要 cmd /v:on。我的最终行是 cmd /v:on /c Echo ^^!text^^!|FindStr /r /C:"^.*%%SOME_VAR%%;.*$" >NUL - jeremfg

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