Я не был уверен, должен ли я отредактировать свой вопрос или опубликовать его как ответ.
Я уже смутно знал, что канал выполняет как левую, так и правую стороны каждый в своей "сессии" CMD.EXE. Но ответы Аасини и Джеба заставили меня по-настоящему задуматься и изучить, что происходит с трубами. (Спасибо, Джеб, за то, что продемонстрировал, что происходит при подключении к SET / P!)
Я разработал этот сценарий расследования - он помогает многое объяснить, но также демонстрирует странное и неожиданное поведение. Я выложу сценарий с последующим выводом. В заключение я приведу некоторый анализ.
@echo off
cls
setlocal disableDelayedExpansion
set var1=value1
set "var2="
setlocal enableDelayedExpansion
echo on
@echo NO PIPE - delayed expansion is ON
echo 1: %var1%, %var2%, !var1!, !var2!
(echo 2: %var1%, %var2%, !var1!, !var2!)
@echo(
@echo PIPE LEFT SIDE - Delayed expansion is ON
echo 1L: %%var1%%, %%var2%%, !var1!, !var2! | more
(echo 2L: %%var1%%, %%var2%%, !var1!, !var2!) | more
(setlocal enableDelayedExpansion & echo 3L: %%var1%%, %%var2%%, !var1!, !var2!) | more
(cmd /v:on /c echo 4L: %%var1%%, %%var2%%, !var1!, !var2!) | more
cmd /v:on /c echo 5L: %%var1%%, %%var2%%, !var1!, !var2! | more
@endlocal
@echo(
@echo Delayed expansion is now OFF
(cmd /v:on /c echo 6L: %%var1%%, %%var2%%, !var1!, !var2!) | more
cmd /v:on /c echo 7L: %%var1%%, %%var2%%, !var1!, !var2! | more
@setlocal enableDelayedExpansion
@echo(
@echo PIPE RIGHT SIDE - delayed expansion is ON
echo junk | echo 1R: %%var1%%, %%var2%%, !var1!, !var2!
echo junk | (echo 2R: %%var1%%, %%var2%%, !var1!, !var2!)
echo junk | (setlocal enableDelayedExpansion & echo 3R: %%var1%%, %%var2%%, !var1!, !var2!)
echo junk | (cmd /v:on /c echo 4R: %%var1%%, %%var2%%, !var1!, !var2!)
echo junk | cmd /v:on /c echo 5R: %%var1%%, %%var2%%, !var1!, !var2!
@endlocal
@echo(
@echo Delayed expansion is now OFF
echo junk | (cmd /v:on /c echo 6R: %%var1%%, %%var2%%, !var1!, !var2!)
echo junk | cmd /v:on /c echo 7R: %%var1%%, %%var2%%, !var1!, !var2!
Вот вывод
NO PIPE - delayed expansion is ON
C:\test>echo 1: value1, , !var1!, !var2!
1: value1, , value1,
C:\test>(echo 2: value1, , !var1!, !var2! )
2: value1, , value1,
PIPE LEFT SIDE - Delayed expansion is ON
C:\test>echo 1L: %var1%, %var2%, !var1!, !var2! | more
1L: value1, %var2%, value1,
C:\test>(echo 2L: %var1%, %var2%, !var1!, !var2! ) | more
2L: value1, %var2%, !var1!, !var2!
C:\test>(setlocal enableDelayedExpansion & echo 3L: %var1%, %var2%, !var1!, !var2! ) | more
3L: value1, %var2%, !var1!, !var2!
C:\test>(cmd /v:on /c echo 4L: %var1%, %var2%, !var1!, !var2! ) | more
4L: value1, %var2%, value1, !var2!
C:\test>cmd /v:on /c echo 5L: %var1%, %var2%, !var1!, !var2! | more
5L: value1, %var2%, value1,
Delayed expansion is now OFF
C:\test>(cmd /v:on /c echo 6L: %var1%, %var2%, !var1!, !var2! ) | more
6L: value1, %var2%, value1, !var2!
C:\test>cmd /v:on /c echo 7L: %var1%, %var2%, !var1!, !var2! | more
7L: value1, %var2%, value1, !var2!
PIPE RIGHT SIDE - delayed expansion is ON
C:\test>echo junk | echo 1R: %var1%, %var2%, !var1!, !var2!
1R: value1, %var2%, value1,
C:\test>echo junk | (echo 2R: %var1%, %var2%, !var1!, !var2! )
2R: value1, %var2%, !var1!, !var2!
C:\test>echo junk | (setlocal enableDelayedExpansion & echo 3R: %var1%, %var2%, !var1!, !var2! )
3R: value1, %var2%, !var1!, !var2!
C:\test>echo junk | (cmd /v:on /c echo 4R: %var1%, %var2%, !var1!, !var2! )
4R: value1, %var2%, value1, !var2!
C:\test>echo junk | cmd /v:on /c echo 5R: %var1%, %var2%, !var1!, !var2!
5R: value1, %var2%, value1,
Delayed expansion is now OFF
C:\test>echo junk | (cmd /v:on /c echo 6R: %var1%, %var2%, !var1!, !var2! )
6R: value1, %var2%, value1, !var2!
C:\test>echo junk | cmd /v:on /c echo 7R: %var1%, %var2%, !var1!, !var2!
7R: value1, %var2%, value1, !var2!
Я протестировал левую и правую сторону трубы, чтобы продемонстрировать, что обработка симметрична с обеих сторон.
Испытания 1 и 2 показывают, что круглые скобки не влияют на задержку расширения в обычных пакетных условиях.
Тесты 1L, 1R: Задержка расширения работает, как и ожидалось. Var2 не определено, поэтому% var2% и! Var2! Выходные данные демонстрируют, что команды выполняются в контексте командной строки, а не в пакетном контексте. Другими словами, правила синтаксического анализа командной строки используются вместо пакетного анализа. (см. Как интерпретирует сценарии интерпретатора команд Windows (CMD.EXE)? ) РЕДАКТИРОВАТЬ -! VAR2! раскрывается в контексте родительского пакета
Тесты 2L, 2R: скобки отключают отложенное расширение! Очень странно и неожиданно для меня. Редактировать - Джеб считает это ошибкой MS или недостатком дизайна. Я согласен, кажется, нет разумной причины для непоследовательного поведения
Тесты 3L, 3R: setlocal EnableDelayedExpansion
не работает. Но это ожидается, потому что мы находимся в контексте командной строки. setlocal
работает только в пакетном контексте.
Тесты 4L, 4R: Задержанное расширение изначально включено, но скобки отключают его. CMD /V:ON
повторно включает отложенное расширение, и все работает как положено. У нас все еще есть контекст командной строки и вывод, как и ожидалось.
Тесты 5L, 5R: Почти то же самое, что и 4L, 4R, за исключением того, что отложенное расширение уже включено при выполнении CMD /V:on
. % var2% дает ожидаемый вывод контекста командной строки. Но! Var2! вывод пуст, что ожидается в пакетном контексте. Это еще одно очень странное и неожиданное поведение. Редактировать - на самом деле это имеет смысл теперь, когда я знаю! Var2! раскрывается в контексте родительского пакета
Тесты 6L, 6R, 7L, 7R: Они аналогичны тестам 4L / R, 5L / R, за исключением того, что теперь отложенное расширение начинается отключенным. На этот раз все 4 сценария дают ожидаемый! Var2! пакетный контекстный вывод.
Если кто-то может дать логическое объяснение результатов 2L, 2R и 5L, 5R, тогда я выберу это в качестве ответа на мой первоначальный вопрос. В противном случае я, вероятно, приму этот пост в качестве ответа (на самом деле это скорее наблюдение за происходящим, чем ответ) Редактировать - джеб его прибил!
<ч />
Приложение: В ответ на комментарий Джеба - это еще одно свидетельство того, что конвейерные команды внутри пакета выполняются в контексте командной строки, а не в контексте пакета.
Этот пакетный скрипт:
@echo on
call echo batch context %%%%
call echo cmd line context %%%% | more
дает этот вывод:
C:\test>call echo batch context %%
batch context %
C:\test>call echo cmd line context %% | more
cmd line context %%
<ч />
Окончательное дополнение
Я добавил несколько дополнительных тестов и результатов, которые пока демонстрируют все результаты. Я также демонстрирую, что расширение переменной FOR происходит перед обработкой канала. Наконец, я показываю некоторые интересные побочные эффекты обработки канала, когда многострочный блок свернут в одну строку.
@echo off
cls
setlocal disableDelayedExpansion
set var1=value1
set "var2="
setlocal enableDelayedExpansion
echo on
@echo(
@echo Delayed expansion is ON
echo 1: %%, %%var1%%, %%var2%%, !var1!, ^^^!var1^^^!, !var2!, ^^^!var2^^^!, %%cmdcmdline%% | more
(echo 2: %%, %%var1%%, %%var2%%, !var1!, ^^^!var1^^^! !var2!, %%cmdcmdline%%) | more
for %%a in (Z) do (echo 3: %%a %%, %%var1%%, %%var2%%, !var1!, ^^^!var1^^^! !var2!, %%cmdcmdline%%) | more
(
echo 4: part1
set "var2=var2Value
set var2
echo "
set var2
)
(
echo 5: part1
set "var2=var2Value
set var2
echo "
set var2
echo --- begin cmdcmdline ---
echo %%cmdcmdline%%
echo --- end cmdcmdline ---
) | more
(
echo 6: part1
rem Only this line remarked
echo part2
)
(
echo 7: part1
rem This kills the entire block because the closing ) is remarked!
echo part2
) | more
Вот вывод
Delayed expansion is ON
C:\test>echo 1: %, %var1%, %var2%, !var1!, ^!var1^!, !var2!, ^!var2^!, %cmdcmdline% | more
1: %, value1, %var2%, value1, !var1!, , !var2!, C:\Windows\system32\cmd.exe /S /D /c" echo 1: %, %var1%, %var2%, value1, !var1!, , !var2!, %cmdcmdline% "
C:\test>(echo 2: %, %var1%, %var2%, !var1!, ^!var1^! !var2!, %cmdcmdline% ) | more
2: %, value1, %var2%, !var1!, !var1! !var2!, C:\Windows\system32\cmd.exe /S /D /c" ( echo 2: %, %var1%, %var2%, !var1!, ^!var1^! !var2!, %cmdcmdline% )"
C:\test>for %a in (Z) do (echo 3: %a %, %var1%, %var2%, !var1!, ^!var1^! !var2!, %cmdcmdline% ) | more
C:\test>(echo 3: Z %, %var1%, %var2%, !var1!, ^!var1^! !var2!, %cmdcmdline% ) | more
3: Z %, value1, %var2%, !var1!, !var1! !var2!, C:\Windows\system32\cmd.exe /S /D /c" ( echo 3: Z %, %var1%, %var2%, !var1!, ^!var1^! !var2!, %cmdcmdline% )"
C:\test>(
echo 4: part1
set "var2=var2Value
set var2
echo "
set var2
)
4: part1
var2=var2Value
"
var2=var2Value
C:\test>(
echo 5: part1
set "var2=var2Value
set var2
echo "
set var2
echo --- begin cmdcmdline ---
echo %cmdcmdline%
echo --- end cmdcmdline ---
) | more
5: part1
var2=var2Value & set var2 & echo
--- begin cmdcmdline ---
C:\Windows\system32\cmd.exe /S /D /c" ( echo 5: part1 & set "var2=var2Value
var2=var2Value & set var2 & echo
" & set var2 & echo --- begin cmdcmdline --- & echo %cmdcmdline% & echo --- end cmdcmdline --- )"
--- end cmdcmdline ---
C:\test>(
echo 6: part1
rem Only this line remarked
echo part2
)
6: part1
part2
C:\test>(echo %cmdcmdline% & (
echo 7: part1
rem This kills the entire block because the closing ) is remarked!
echo part2
) ) | more
Тесты 1: и 2: обобщение всех поведений, и трюк %% cmdcmdline %% действительно помогает продемонстрировать, что происходит.
Тест 3: демонстрирует, что расширение переменной FOR по-прежнему работает с конвейером.
Тесты 4: / 5: и 6: / 7: показывают интересные побочные эффекты работы труб с многострочными блоками. Осторожно!
Я должен верить, что вычисление escape-последовательностей в сложных сценариях трубы будет кошмаром.