It's obvious :-)
Here are different phases of the parser involved.
The first important phase is here the special character phase, a caret escapes the next characters, important for ^|&<>
characters.
The line will be reduced from
echo|set /P=!foo! ^^^^^| !bar!
to
echo|set /P=!foo! ^^| !bar!
As one caret escapes the next, and the last caret escapes the pipe.
The next important phase here is the delayed expansion phase, this phase only occours when at least one !
is in the line.
In this phase carets escapes also the next character, but here it's only necessary for the caret itself and the !
, and in this phase the delayed variables are expanded.
From
echo|set /P=!foo! ^^| !bar!
to
echo|set /P=Hello world ^| Why why
As you use a pipe here, both sides of the pipe will be transfered to a new cmd.exe process and there the commands will be parsed again.
cmd.exe /c "echo"
and cmd /c "set /P=Hello world ^| Why why
Only set /P=Hello world ^| Why why
is relavant now.
Again in the special character phase the one caret escapes the pipe character to
set /P=Hello world | Why why
The delayed expansion phase will not occour, first as delayed expansion is disabled in the new cmd.exe (by default) and second as there aren't any !
in the line.
That's all!
Btw.
Spawning two processes here is not necessary, a redirection is simpler and faster
<nul set /P=!foo! ^^^| !bar!
Without delayed expansion, you only need one caret
<nul set /P=FOO ^| BAR
Or look at the difference of
setlocal EnableDelayedExpansion
echo "carets ^^^"
echo "carets ^^^" !