Appending newlines to a variable to be "echo"ed out
To create a new line variable is a good start. But you should use it in a different way.
Percent expansion doesn't work quite well with newlines in variables, it can be done, but it's quite complex.
But delayed expansion flawlessly works with any characters
@echo off
setlocal EnableDelayedExpansion
(set \n=^
%=empty line=%
)
echo Line1!\n!Line2
set "multiline_var=First Line!\n!Second Line"
echo !multiline_var!
First of all, delayed expansion does not affect the immediately expanded new-lines (or correctly spoken, line-feed characters), because you are using immediate (%
-)expansion for them.
The major issue is that %
-expansion happens so early, which seems to interfere with recognition of line-breaks when a variable contains such.
Everything behind a line-break becomes dismissed every time the parser expands a %
-variable containing line-breaks, so they must be escaped, leading to manifold escaping sequences. This can be demonstrated by the following script:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem // Define a line-feed character:
set \n=^
rem // Define an escaped line-feed character:
set NL=^^^%\n%%\n%^%\n%%\n%
::rem // Redefine the escaped new-line character; it does not change, because the parser first expands `%`-variables before it recognises line-breaks in them:
::set NL=^^%NL%%NL%
rem // Define multi-escaped sequences needed when the same expanded value passes through the parser several times:
set NLNL=^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%
set NLNLNL=^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%
::set NLNLNL=^^^^^^^^^^^^^^^^^^^^^^^^%NLNL%%NLNL%^^^^^^^^%NLNL%%NLNL%
echo ---- DEFINITION ----
set NL
echo --------------------
echo !\n! ***** IMMEDIATE EXPANSION ***** !\n!
rem // The more parser passes the more escaping is needed:
echo -- VARIABLE CHECK --
set errMsg=This would be the first custom error message%NLNLNL%
set errMsg
set errMsg=%errMsg%This would be the second custom error message%NLNL%
set errMsg
set errMsg=%errMsg%This would be the third custom error message%NL%
set errMsg
echo -- MESSAGE OUTPUT --
echo %errMsg%
echo --------------------
echo !\n! ***** `CALL` EXPANSION ***** !\n!
echo -- VARIABLE CHECK --
set errMsg=This would be the first custom error message%%NL%%
set errMsg
set errMsg=%errMsg%This would be the second custom error message%%NL%%
set errMsg
set errMsg=%errMsg%This would be the third custom error message%%NL%%
set errMsg
echo -- MESSAGE OUTPUT --
call echo %errMsg%
echo --------------------
echo !\n! ***** DELAYED EXPANSION ***** !\n!
echo -- VARIABLE CHECK --
set errMsg=This would be the first custom error message!\n!
set errMsg
set errMsg=!errMsg!This would be the second custom error message!\n!
set errMsg
set errMsg=!errMsg!This would be the third custom error message!\n!
set errMsg
echo -- MESSAGE OUTPUT --
echo !errMsg!
echo --------------------
endlocal
exit /B
This is the related Command Prompt output:
---- DEFINITION ---- NL=^ NLNL=^^^^^^^ ^ ^^^ ^ NLNLNL=^^^^^^^^^^^^^^^ ^ ^^^ ^ ^^^^^^^ ^ ^^^ ^ -------------------- ***** IMMEDIATE EXPANSION ***** -- VARIABLE CHECK -- errMsg=This would be the first custom error message^^^^^^^ ^ ^^^ ^ errMsg=This would be the first custom error message^^^ ^ This would be the second custom error message^^^ ^ errMsg=This would be the first custom error message^ This would be the second custom error message^ This would be the third custom error message -- MESSAGE OUTPUT -- This would be the first custom error message This would be the second custom error message This would be the third custom error message -------------------- ***** `CALL` EXPANSION ***** -- VARIABLE CHECK -- errMsg=This would be the first custom error message%NL% errMsg=This would be the first custom error message%NL%This would be the second custom error message%NL% errMsg=This would be the first custom error message%NL%This would be the second custom error message%NL%This would be the third custom error message%NL% -- MESSAGE OUTPUT -- This would be the first custom error message This would be the second custom error message This would be the third custom error message -------------------- ***** DELAYED EXPANSION ***** -- VARIABLE CHECK -- errMsg=This would be the first custom error message errMsg=This would be the first custom error message This would be the second custom error message errMsg=This would be the first custom error message This would be the second custom error message This would be the third custom error message -- MESSAGE OUTPUT -- This would be the first custom error message This would be the second custom error message This would be the third custom error message --------------------
In the IMMEDIATE EXPANSION
section complicated escape sequences are required to maintain the line-breaks when command lines pass through the parsing process multiple times.
The
section illustrates a simpler way, using the CALL
EXPANSIONcall
command, which introduces another parsing phase, which lets the literal string portion %NL%
be expanded, which is actually contained in the errMsg
variable rather than line-breaks. Note that call
is slow and has got side-effects, like caret-(^
-)doubling and loss of &
, |
, <
and >
under certain circumstances.
The best option is certainly to use delayed expansion as shown through the DELAYED EXPANSION
section, because this allows the variable errMsg
to actually contain the line-breaks and to safely expand them without being recognised by the parser too early.