1

I'm just curious on one specific case of the caret escape in Windows batch:

Please save the following code as a file ttt.bat, and run ttt.bat on command prompt, why do I have to put 5 repetitions of the caret (or carat?) character ^ to make this "no new line echo" trick work? I only want to put a pipe '|' in the echoed string.

There is some documentation on the escaping thing for Windows batch, but for a case study purpose, can you explain what's the meanings of every caret character used in this example? Thanks!

@echo off

SetLocal EnableDelayedExpansion

set foo=Hello world
set bar=Why why

echo|set /P=!foo! ^^^^^| !bar!

EndLocal
Community
  • 1
  • 1
zerox
  • 307
  • 3
  • 11
  • Well, if you would write `echo|set /P "=!foo! | !bar!"` then you would not need to escape pipe character at all as being part of a double quoted string. `set /P "variable=prompt text"` respectively `set "variable=string value"` are the preferred methods to assign strings to an environment variable. See [How to set environment variables with spaces?](http://stackoverflow.com/a/34402887/3074564) and execute in a command prompt window `cmd /?` and read at least the last paragraph on last output help page. – Mofi Jul 09 '16 at 11:00
  • I thought the shell would print the double quotes as well, thank you for pointing out this! – zerox Jul 09 '16 at 14:42

1 Answers1

5

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 ^^^" !
Community
  • 1
  • 1
jeb
  • 78,592
  • 17
  • 171
  • 225
  • Did I get it right: durin the delayed expansion parse phase, `^^` is escaped to `^` only in case at least one `!` appears in the parsed command line, so when there is no such character, `^^` remains as is? – aschipfl Jul 08 '16 at 16:37