Почему я не могу получить подстроку переменной с задержкой расширения в выражении if?

Почему строковое манипулирование работает inline в выражении if без использования переменных с задержкой расширения, но в противном случае не работает. Например:

set test=testString
if %test:~0,4%==test echo Success

Это работает правильно; возвращая Success. Однако, если я сделаю следующее:

setLocal enableDelayedExpansion
set test=testString
if !test:~0,4!==test echo Success

Я получаю сообщение об ошибке - 4!==test was unexpected at this time.

Очевидно, вы можете обойти это, сделав что-то вроде set comp=!test:~0,4!, а затем просто используя переменную !comp! в инструкции if.

Ответ 1

npocmaka правильно диагностировал проблему, что команда IF получает свою собственную специальную фазу синтаксического анализа, а разделители токенов внутри расширения переменной создают проблемы.

Замещение также вызывает проблему:

:: This fails
if !test:String=!==test echo Success

Причина, по которой нормальное расширение работает, происходит потому, что это происходит до синтаксического анализа IF, но замедленное расширение происходит после синтаксического анализа IF.

Альтернативой использованию кавычек является устранение проблемных символов.

:: Both of these work
if !test:~0^,4!==test echo Success
if !test:String^=!==test echo Success

Ответ 2

,;=<tab> и <space> являются разделителями для cmd.exe и во многих случаях игнорируются, если они не входят в кавычки и действуют как пустое пространство. Вероятно, в этом случае , берется как конец первого операнда и IF удивляется тому, что нет действительного оператора сравнения, например это напечатает yep:

if a   ;==;,,=a echo yep

(но не будет работать, если в первой части операндов есть знак равенства)

Но это не будет:

if "a ;" == ";,,=a" echo yep

поэтому для создания правильного выражения IF, когда вы используете запятую, вам нужны кавычки. И это будет работать

setLocal enableDelayedExpansion
set test=testString
if "!test:~0,4!" == "test" echo Success

Без замедленной замены замена производится немедленно, и это будет работать без кавычек:

set test=testString
setlocal disableDelayedExpansion
if %test:~0,4% == test echo Succes
endlocal

По той же причине это будет восприниматься как неправильное выражение синтаксиса (см. комментарий jeb):

set "test="
setlocal disableDelayedExpansion
if %test% == test echo Succes
endlocal

Может быть, это не полный ответ, но должен быть близок. Поскольку обе echo !test:~0,4! и echo %test:~0,4% будут работать без кавычек, остается вопрос, почему именно IF терпит неудачу - может потому что команда IF использует свой собственный синтаксический анализатор

В качестве вывода - всегда полезно использовать кавычки при сравнении строк с IF:

  • с задержкой запятой и точкой с запятой вызовет проблемы.
  • без замедленного расширения undefined переменные вызовут проблемы.