Почему в этом случае не работает отложенное расширение в командном файле? - PullRequest
3 голосов
/ 25 октября 2011

Этот код

@echo off
setlocal EnableDelayedExpansion

set myvar=first
set first=second

echo myvar:!myvar!
set myvar=!myvar!
echo myvar:!myvar!

дает

myvar:first
myvar:first

в Windows Vista с пакетом обновления 2 (SP2).

Ожидаемый результат:

myvar:first
myvar:second

Почему разница и как получить желаемый эффект?

Ответы [ 3 ]

9 голосов
/ 26 октября 2011

Проблема в том, что set myvar=!myvar! расширяется до set myvar=first,
вы устанавливаете его с тем же содержимым, а затем просите echo myvar:!myvar! показать содержимое myvar.

Я попробуюдобавить еще несколько объяснений, даже если Aacini и shf301 уже ответили на вопрос.

Оба показали двойное расширение с конструкцией !%var%!, и Аасини объяснил, почему оно может работать и почему не работает обратная версия %!var!%.

ИМХО, есть четыре разныхрасширения.
Отсроченное расширение:
Как объяснил Аасини, отложенное расширение безопасно для любых специальных символов в содержимом (оно может обрабатывать ВСЕ символы от 0x01 до 0xFF).

Процентное расширение:
Процентное расширение не может обрабатывать или удалять некоторые символы (даже с экранированием).
Это может быть полезно для простого содержимого, поскольку оно может расширятьсяпеременные после endlocal барьера.

setlocal
set "myVar=simple content"
(
endlocal
set result=%myVar%
)

Расширение FOR-Loop-Parameters:
Безопасно, если задержанное расширение отключено, иначе фаза отложенного расширениявыполняется после раскрытия переменных %% a.
Это может быть полезно, так как может расширять переменные после endlocal барьера

setlocal EnableDelayedExpansion
set "var=complex content &<>!"
for /F "delims=" %%A in ("!var!") DO (
  endlocal
  set "result=%%A"
)

SET Расширение:
set var расширяет также переменную, и она всегда безопасна и работает независимо от режима отложенного расширения.

Aacini только что объяснил, как работает конструкция call %%%var%%%, я только хочу дать несколько дополнительных замечаний.
call можно наращивать, вы можете использовать многие из них, и каждый перезапускает анализатор.

set "var=%%var%%#"
call call call call call echo %var%

приводит к %var%######

Но call имеет много недостатков / побочных эффектов!
Каждый вызов удваивает все кареты ^
Вы можете сказать: «Привет, я»Я проверил это, и я не вижу никакого удвоения "

call call call call echo ^^

результат ^

Тем не менее, это правда, но в основном это скрыто, так как каждый перезапуск также имеет фазу специального символа, гдеCarets экранирует следующий символ, но вы можете увидеть эффект удвоения с

call call call call echo "^^"^^

result "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"^

Даже если расширение вызова перезапускает анализатор, вы никогда не сможете использовать отложенное расширение влюбая фаза (только в первой).

call перестает работать, если обнаруживает неэкранированные специальные символы.

echo you ^& me
call echo you & me
call echo you ^& me
call echo you ^^& me
call echo you ^^^& me

Только первые результаты на выходе you & me, все остальные сбои.

Другая проблема заключается в том, что вызов очень медленный, call set var=content примерно в 50 раз медленнее set var=content, причина в том, что вызов пытается запустить внешнюю программу.

@echo off
setlocal
(
   echo echo *** External batch, parameters are '%%*'
) > set.bat
set "var="
call set var=hello
set var

Iнадеюсь, что это будет немного интересно ...
И если вы хотите углубиться в подробности, вы можете прочитать ЗВОНИТЕ меня, или лучше избегайте звонков
и Как работает интерпретатор команд Windows(CMD.EXE) разбирать скрипты?

2 голосов
/ 25 октября 2011

Эта проблема не связана напрямую с отложенным расширением переменной, но с тем фактом, что требуются два расширения значения: первое дает имя переменной, а второе должно заменить это имя ее значением.Прямой способ сделать это - через два раскрытия в одной строке, как показано в предыдущем ответе: set myvar=!%myvar%!, который работает, потому что расширение% var% выполняется до того, как командная строка проанализирована на выполнение, тогда как! Var!раскрытие выполняется позже, непосредственно перед выполнением команды (отсюда и «задержанное» имя).Это означает, что расширение% var% может содержать части команды и может вызывать синтаксические ошибки, но! Var!не.Например, if %var%==value ... вызывает ошибку, если переменная пуста или имеет пробелы, но if !var!==value ... никогда не вызывает синтаксическую ошибку.

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

echo myvar:%myvar%
echo set myvar=%%%myvar%%%> auxiliary.bat
call auxiliary
echo myvar:%myvar%

Предыдущий метод может использоваться для расширения третьего или даже более глубокого уровня и даже может быть объединен с отложенными расширениями для созданиякомплексное управление стоимостью.Это не просто любопытство, а ключ к доступу к элементам массива или связанным спискам.Например:

set month[1]=January
set month[2]=February
. . .
set month[12]=December
for /f "tokens=1-3 delims=/" %%a in ("%date%") do echo Today is !month[%%a]! %%b, %%c
1 голос
/ 25 октября 2011

То, что вы пытаетесь сделать, не сработает - отложенное расширение только изменяет поведение расширения переменной внутри блока. Он не позволяет вам использовать псевдонимы / вложения (из-за отсутствия лучшего слова), которые вы пытаетесь.

set myvar=first устанавливает переменную myvar в текст «first». set first=second устанавливает переменную first в текст "second. Между этими двумя строками нет связи. myvar никогда не будет вычислять что-то, для чего оно не было явно установлено.

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


* Редактировать *

ОК, взглянув на ваш ответ Я вижу, как это работает, вы можете получить желаемый результат с помощью этого:

@echo off
setlocal EnableDelayedExpansion

set myvar=first
set first=second

echo myvar:%myvar%
set myvar=!%myvar%!
echo myvar:%myvar%

Так что магия, кажется, происходит из-за того, как происходит стандартное и отложенное расширение. Строка set myvar=!%myvar%! сначала расширяется стандартным расширителем до set myvar=!first! (вы увидите это, если запустите сценарий с echo on). Затем запаздывающий расширитель запускается и расширяет !first до «секунды» и устанавливает myvar в это значение.

Я понятия не имею, документировано ли это поведение относительно того, как должно работать стандартное и отложенное расширение, или просто деталь реализации (что означает, что оно может сломаться в будущем)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...