3

I've been working in Linux for the last 12 years, worked with Windows and command lines before that and have had to recently resurrect those batch file skills for a little easy to use / edit utility. However, I'm having some issues in finding out how to build up a string variable with newline characters (the equivalent of Linux's echo -e "Line1\nLine2")

Basically my utility asks three questions of a user and checks the validity of the inputs. Each input has a slightly different "error message" if the validity fails. I then have a check to see if the errMsg variable contains anything and if it does, it lists the collated error messages from the 3 validity checks. This all works perfectly with the exception of the error message is on one line and I'd like to put each error on it's own line. I then "merely" add newlines to the string ... and that's the crux of this question.

I've used this link as a reference point and with a basic string, the new lines appear as expected. However, when I use a variable, the new lines don't appear and I was hoping someone could explain to me why.

I have the following code snippet

@echo off
setlocal enableextensions enabledelayedexpansion

set \n=^


set NL=^^^%\n%%\n%^%\n%%\n%

echo This does indeed put in a newline%NL%and this is the 2nd line
:: output follows:
::This does indeed put in a newline
::and this is the 2nd line

set var=Line1%NL%
set var=%var%Line2%NL%
:: output follows:
::Line1Line2

set errMsg=This would be the first custom error message%NL%
set errMsg=%errMsg%This would be the second custom error message%NL%
set errMsg=%errMsg%This would be the third custom error message%NL%
:: output follows
::This would be the first custom error messageThis would be the third custom error message

Obviously at this point I was expecting the Line1Line2 example to be split across two lines, and then the last example to have all the custom error messages across three lines (whereas in reality, it's missing the 2nd / middle error message too)

Can anyone explain to me why this isn't working as expected? I'm wondering if it's related to the delayed expansion (which I believe I need: this snippet is part of a longer file that does need the delayed expansion). Is there a better (or merely alternative) way of completing what I'm looking for?!

bnoeafk
  • 489
  • 4
  • 16
  • 1
    No, the issue is not related to delayed expansion since you are not using it; you would if you put `!\n!` instead of `%NL%`, which would do what you eventually looking for… – aschipfl Jan 21 '22 at 22:44

2 Answers2

2

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!
jeb
  • 78,592
  • 17
  • 171
  • 225
  • I guess I'm nearly there, but I have to set my "multiline_var" over multiple lines of code as there's an `IF` statement which dictates if it needs to be appended or not. So using your example code, I'd invoke it as follows: if "%testA%" == "Y" set "multiline=Test A is Yes!\n!" if "%testB%" == "Y" set "multiline=%multiline%Test B is Yes!\n!" if "%testC%" == "Y" set "multiline=%multiline%Test C is Yes!\n!" echo "%multiline%" As you can see I'm appending to the multiline variable depending on the IFs - but that doesn't seem to work – bnoeafk Jan 21 '22 at 22:58
  • @bnoeafk, apply delayed expansion also for `!multiline!`, otherwise you lose the line-breaks or new-lines… – aschipfl Jan 21 '22 at 23:02
1

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 CALL EXPANSION section illustrates a simpler way, using the call 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.

aschipfl
  • 33,626
  • 12
  • 54
  • 99