Пакетный файл - Как использовать FindStr с процентами

У меня есть командный файл со следующим заголовком:

@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 является существующей переменной среды, это то, что я получаю. Если я удалю переменную, я получу ожидаемый успех.

Что здесь происходит? Мне нужно сбежать от чего-то большего? Как я могу получить успешную находку на втором, если переменная существует? Меня интересует только текстовый поиск, в котором искомый текст содержит символ % и совпадает с существующим именем переменной.

Кстати, источником для сравнения в конечном итоге тоже будет переменная, в которую я загрузил PATH, прочитанный из реестра Windows. Итак, в конечном итоге строка, которую я ищу, станет /C:"^.%%SOME_VAR%%;.*$"

Моя переменная PATH выглядит так:

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

person jeremfg    schedule 28.05.2015    source источник


Ответы (1)


Да, требуется еще один уровень побега, потому что каждая сторона конвейера выполняется через 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!")
)

--ВЫХОД--

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
person dbenham    schedule 29.05.2015
comment
Спасибо, это очень ясное объяснение, и оно работает... Но как я могу это сделать, когда это переменная, которую я повторяю, со значением, таким как %SOME_VAR%;. Я не могу создать строку, которую я ищу, и добавить к ней символы вставки. - person jeremfg; 02.06.2015
comment
Очевидно, хороший ответ, но я не вижу причин использовать здесь cmd /v:on. Почему бы просто не использовать echo %%text%% | findstr "%%search%%"? Как будто все дело в обычных именах переменных с процентами. Тогда процентное расширение тоже безопасно - person jeb; 03.06.2015
comment
@jeb - Дох! Вы правы насчет правой стороны. Я зря усложнил поиск. Но левая сторона имеет дело со значением PATH в реестре и может иметь содержимое, небезопасное при обычном расширении. - person dbenham; 03.06.2015
comment
@dbenham - Да, я не использовал/не нуждался в cmd /v:on справа, когда комментировал выше. Моя последняя строка cmd /v:on /c Echo ^^!text^^!|FindStr /r /C:"^.*%%SOME_VAR%%;.*$" >NUL - person jeremfg; 04.06.2015