Mysterious happenings in Windows Batch, Not escaping correctly? - windows

This is part of my windows batch script:
#echo cd /data/backup/> working\grab__test
#echo line1 'length($0)^>7{$0^=substr^($0,1 >> working\grab__test
This is not the real world code, just the isolated parts to show the problem.
They should be writing lines to file grab__test. What gets written to grab__test is:
line1 'length($0
What is going on?? If I don't escape the > and = I get an even wierder error:
1 was unexpected at this time.
What?? Why THAT 1 at the end of the line in particular?? There are other numerical digits, why not 7? Why does the second line that has the > escaped keep writing to the file?
Arg!

The command line
#echo line1 'length($0)^>7{$0^=substr^($0,1 >> working\grab__test
works as expected outside of a command block starting with ( and ending with matching ). It is not necessary to escape the equal sign = and opening parenthesis ( in this command line. It is only necessary to escape right angle bracket > as being otherwise interpreted as redirection operator.
But inside a command block it is not working as expect because Windows command interpreter interprets not escaped closing parenthesis ) as end of command block started somewhere above with (. So within a command block each ) in an argument string not enclosed in double quotes like here must be escaped with caret character ^ to be interpreted as literal character.
Here is a code demonstrating this difference.
#echo off
set "OutputFile=%TEMP%\Test.tmp"
echo Line 1: 'length($0)^>7{$0=substr($0,1 >"%OutputFile%"
>>"%OutputFile%" echo Line 2: 'length($0)^>7{$0=substr($0,1
(
>>"%OutputFile%" echo Line 3: 'length($0^)^>7{$0=substr($0,1
)
for /F "usebackq delims=" %%I in ("%OutputFile%") do echo "%%I"
del "%OutputFile%"
set "OutputFile="
The output is:
"Line 1: 'length($0)>7{$0=substr($0,1 "
"Line 2: 'length($0)>7{$0=substr($0,1"
"Line 3: 'length($0)>7{$0=substr($0,1"
The code uses a FOR loop to output each line separately with command ECHO enclosed in double quotes instead of type "%OutputFile%" to show the difference between line 1 and line 2.
There is a space between 1 at end of string to write into the file and the redirection operator > on the command line writing line 1 into the file. This space is necessary as otherwise the character 1 would not be written into the file. But this space character belongs to the string which ECHO outputs and results therefore in a trailing space in the file.
The solution to avoid this trailing space is specifying the redirection left to the ECHO command as done on the command lines writing line 2 and 3 into the file.
Let us debug this batch file by modifying the first line to #echo on and run the batch file from within a command prompt window instead of double clicking on it. (Environment variable TEMP is defined for this example output with C:\Windows\Temp.)
set "OutputFile=C:\Windows\Temp\Test.tmp"
echo Line 1: 'length($0)>7{$0=substr($0,1 1>"C:\Windows\Temp\Test.tmp"
echo Line 2: 'length($0)>7{$0=substr($0,1 1>>"C:\Windows\Temp\Test.tmp"
(echo Line 3: 'length($0)>7{$0=substr($0,1 1>>"C:\Windows\Temp\Test.tmp"
)
for /F "usebackq delims=" %I in ("C:\Windows\Temp\Test.tmp") do echo "%I"
echo "Line 1: 'length($0)>7{$0=substr($0,1 "
"Line 1: 'length($0)>7{$0=substr($0,1 "
echo "Line 2: 'length($0)>7{$0=substr($0,1"
"Line 2: 'length($0)>7{$0=substr($0,1"
echo "Line 3: 'length($0)>7{$0=substr($0,1"
"Line 3: 'length($0)>7{$0=substr($0,1"
del "C:\Windows\Temp\Test.tmp"
set "OutputFile="
Some command lines really executed after preprocessing each command line or entire command block before execution look different than written in batch file.
On the command line outputting first line into the file > is modified to  1>, i.e. a space and 1 is inserted. So there are now two spaces between $0,1 to write into the file and 1> which redirects STDOUT to the specified file. That is the reason why the first space after $0,1 is also written into the file.
On the command lines output second and third line the redirection specified left to command ECHO is now on right side with  1>> instead of just >> and with just one space between $0,1 and 1>>. For that reason those two lines are written into the file without a trailing space.
Also interesting to see is how the command block is reformatted by Windows command interpreter.
Whenever a batch file is not running as expected it is highly recommended to modify #echo off to #echo on or remove that line or comment it out with command REM (or invalid label ::) and run the batch file from within a command prompt window to see what is really executed by Windows command interpreter after preprocessing each command line and command block.

Related

cmd append to file > extra space in the end

set NUMBER=0
echo 3.3.3.%NUMBER% > "file.txt"
after running this command the content of file.txt is 3.3.3.0 with extra space after the 0
I would like to remove the extra space, how this can be done?
EDIT
I following the answer of bgoldst the extra space is gone, however now I have extra line break. I want to remove it
I found some answers for solving the extra line, however I don't want space not new line in the file
Building on the answer by bgoldst
To remove the space and the line feed:
<nul set /p=3.3.3.%NUMBER%>file.txt
See Windows batch: echo without new line for more information.
The space is introduced by the echo command because there is a space between the %NUMBER% variable and the > redirection operator. The echo command respects trailing whitespace in its command-line.
Thus, you can solve this by removing that space (and you can also remove the quotes, as well as the space between the > and the file.txt, since they are not necessary):
echo 3.3.3.%NUMBER%>file.txt

Echo string to .txt file with multiple lines - with Windows Batch file

I am attempting to create a Windows Batch File that creates a .txt with mulitple lines. I've tried several solutions to insert a line break in the string but no avail. There are other similar questions/answers but none of them address putting the entire string into a text file.
My batch file currently reads:
echo Here is my first line
Here is my second line > myNewTextFile.txt
pause
my goal is to have the text file read:
Here is my first line
Here is my second line
Obviously, this does not work currently, but wondering if anyone knows how to make this happen in a simple fashion?
(
echo Here is my first line
echo Here is my second line
echo Here is my third line
)>"myNewTextFile.txt"
pause
Just repeat the echo and >> for lines after the first. >> means that it should append to a file instead of creating a new file (or overwriting an existing file):
echo Here is my first line > myNewTextFile.txt
echo Here is my second line >> myNewTextFile.txt
echo Here is my third line >> myNewTextFile.txt
pause
Searching for something else, I stumbled on this meanwhile old question, and I have an additional little trick that is worth mentioning, I think.
All solutions have a problem with empty lines and when a line starts with an option for the echo command itself. Compare the output files in these examples:
call :data1 >file1.txt
call :data2 >file2.txt
exit /b
:data1
echo Next line is empty
echo
echo /? this line starts with /?
echo Last line
exit /b
:data2
echo:Next line is empty
echo:
echo:/? this line starts with /?
echo:Last line
exit /b
Now, file1.txt contains:
Next line is empty
ECHO is off.
Displays messages, or turns command-echoing on or off.
ECHO [ON | OFF]
ECHO [message]
Type ECHO without parameters to display the current echo setting.
Last line
While file2.txt contains:
Next line is empty
/? this line starts with /?
Last line
The use of echo: miraculously solves the issues with the output in file1.txt.
Besides the colon, there are other characters that you could 'paste' to echo, among them a dot, a slash, ... Try for yourself.
STEP 1: Enter Line 1 followed by the ^ character.
echo Here is my first line^
STEP 2: Hit RETURN key to get a prompt for more text
echo Here is my first line^
More?
STEP 3: Hit RETURN key once more to get a second prompt for more text
echo Here is my first line^
More?
More?
STEP 4: Continue line 2 from the second prompt
echo Here is my first line^
More?
More? Here is my second line
STEP 5: Hit the RETURN key to get 2 statements displayed on two separate lines
Results:
echo Here is my first line^
More?
More? Here is my second line
Here is my first line
Here is my second line
NOTE
However, if you wish to save this to file, you could add a final STEP.
STEP 6: with the help of the > character, you can append the filename so you save your output to file instead.
echo Here is my first line^
More?
More? Here is my second line >"myNewTextFile.txt"
Example from CMD

Newlines in batchfiles via caret ampersand echo. (^&echo.)

So I learned today that a newline can be set via the following command:
set nl=^&echo.
For example:
set nl=^&echo.
echo Hello%nl%world
yields
Hello
world
But why does this work? What is the significance of ^&?
Place the code inside a .bat file, and do not set echo to off (leave echo on) and you will see how the commands are being expanded and executed.
Batch
set nl=^&echo.
echo One%nl%Two%nl%Three
Output
C:\>set nl=&echo.
C:\>echo One & echo.Two & echo.Three
One
Two
Three
The ^ escapes the & special character so that it is a literal character able to be set inside the nl variable. Then when the nl variable is expanded, the &echo. is inserted.
All is left is to deconstruct the &echo. part. The ampersand & means that a new command starts on the same line. That new command on the same line is echo., which outputs a new line.

Is it possible to put a new line character in an echo line in a batch file? [duplicate]

This question already has answers here:
How can I echo a newline in a batch file?
(24 answers)
Closed 7 years ago.
Is it possible to put a new line character in an echo line in a batch file?
Basically I want to be able to do the equivalent of:
echo Hello\nWorld
You can do this easily enough in Linux, but I can't work out how to do it in Windows.
echo. prints an empty line.
Example:
echo Hello
echo.
echo world
prints
Hello
world
It can be solved with a single echo.
You need a newline character \n for this.
There are multiple ways to get a new line into the echo
1) This sample use the multiline caret to add a newline into the command,
the empty line is required
echo Hello^
world
2) The next solution creates first a variable which contains one single line feed character.
set \n=^
rem ** Two empty lines are required
Or create the new line with a slightly modified version
(set \n=^
%=DONT REMOVE THIS=%
)
And use this character with delayed expansion
setlocal EnableDelayedExpansion
echo Hello!\n!world
To use a line feed character with the percent expansion you need to create a more complex sequence
echo Hello^%\n%%\n%world
Or you can use the New line hack
REM Creating a Newline variable (the two blank lines are required!)
set \n=^
set NL=^^^%\n%%\n%^%\n%%\n%
REM Example Usage:
echo There should be a newline%NL%inserted here.
But only the delayed expansion of the newline works reliable, also inside of quotes.
After a little experimentation I discovered that it is possible to do it without issuing two separate echo commands as described in How can you echo a newline in batch files?. However to make it work you will need a text editor that does not translate CR to CR+LF.
Type:
#echo First Line
then with NumLock on, hold down the ALT key and type 10 on the numeric keypad before releasing ALT (you must use the numeric keypad, not the top-row number keys). This will insert a CR character. Then type the second line. Depending on your editor and how it handles CR compared with CR+LF you may get:
#echo First Line◙Second Line
or
#echo First Line
Second Line
This works from the command line and will work in a batch file so long as the text editor does not translate CR to CR+LF (which Windows/DOS editors do unless you configure them not to). If the CR is converted to CR+LF, or if you use just LF, the second line is interpreted as a new command.
However, I cannot see why this would be preferable over simply:
#echo First Line
#echo Second Line
Ahaha,
I think I've worked out something close enough...
echo hello&echo.&echo world
Produces:
hello
world
echo.
or
echo(
will do the blank new line. Hope this is helpful.
I found this very informative, so wanted to post a better example using the answers provided
This provides a nicely formatted usage message
if "%1" == """" goto usage
:usage
echo USAGE: %0 [Set properties using -D flag] [Ant Task to Run] &
echo. &
echo Availble Command line properties &
echo -------------------------------- &
...
I think it is not possible. You could only try an ascii-character to this:
http://www.asciitable.com/
But this will perhaps crash your batch-file.

How can I echo a newline in a batch file?

How can you you insert a newline from your batch file output?
I want to do something like:
echo hello\nworld
Which would output:
hello
world
Use:
echo hello
echo:
echo world
echo hello & echo.world
This means you could define & echo. as a constant for a newline \n.
Here you go, create a .bat file with the following in it :
#echo off
REM Creating a Newline variable (the two blank lines are required!)
set NLM=^
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
REM Example Usage:
echo There should be a newline%NL%inserted here.
echo.
pause
You should see output like the following:
There should be a newline
inserted here.
Press any key to continue . . .
You only need the code between the REM statements, obviously.
There is a standard feature echo: in cmd/bat-files to write blank line, which emulates a new line in your cmd-output:
#echo off
echo line1
echo:
echo line2
or
#echo line1 & echo: & echo line2
Output of cited above cmd-file:
line1
line2
Like the answer of Ken, but with the use of the delayed expansion.
setlocal EnableDelayedExpansion
(set \n=^
%=Do not remove this line=%
)
echo Line1!\n!Line2
echo Works also with quotes "!\n!line2"
First a single linefeed character is created and assigned to the \n-variable.
This works as the caret at the line end tries to escape the next character, but if this is a Linefeed it is ignored and the next character is read and escaped (even if this is also a linefeed).
Then you need a third linefeed to end the current instruction, else the third line would be appended to the LF-variable.
Even batch files have line endings with CR/LF only the LF are important, as the CR's are removed in this phase of the parser.
The advantage of using the delayed expansion is, that there is no special character handling at all.
echo Line1%LF%Line2 would fail, as the parser stops parsing at single linefeeds.
More explanations are at
SO:Long commands split over multiple lines in Vista/DOS batch (.bat) file
SO:How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Edit: Avoid echo.
This doesn't answer the question, as the question was about single echo that can output multiple lines.
But despite the other answers who suggests the use of echo. to create a new line, it should be noted that echo. is the worst, as it's very slow and it can completly fail, as cmd.exe searches for a file named ECHO and try to start it.
For printing just an empty line, you could use one of
echo,
echo;
echo(
echo/
echo+
echo=
But the use of echo., echo\ or echo: should be avoided, as they can be really slow, depending of the location where the script will be executed, like a network drive.
echo. Enough said.
If you need it in a single line, use the &. For example,
echo Line 1 & echo. & echo line 3
would output as:
Line 1
line 3
Now, say you want something a bit fancier, ...
set n=^&echo.
echo hello %n% world
Outputs
hello
world
Then just throw in a %n% whenever you want a new line in an echo statement. This is more close to your \n used in various languages.
Breakdown
set n= sets the variable n equal to:
^ Nulls out the next symbol to follow:
& Means to do another command on the same line. We don't care about errorlevel(its an echo statement for crying out loud), so no && is needed.
echo. Continues the echo statement.
All of this works because you can actually create variables that are code, and use them inside of other commands. It is sort of like a ghetto function, since batch is not exactly the most advanced of shell scripting languages. This only works because batch's poor usage of variables, not designating between ints, chars, floats, strings, etc naturally.
If you are crafty, you could get this to work with other things. For example, using it to echo a tab
set t=^&echo. ::there are spaces up to the double colon
When echoing something to redirect to a file, multiple echo commands will not work. I think maybe the ">>" redirector is a good choice:
echo hello > temp
echo world >> temp
If you need to put results to a file, you can use:
(echo a & echo: & echo b) > file_containing_multiple_lines.txt
Just like Grimtron suggests - here is a quick example to define it:
#echo off
set newline=^& echo.
echo hello %newline%world
Output
C:\>test.bat
hello
world
You can also do like this,
(for %i in (a b "c d") do #echo %~i)
The output will be,
a
b
c d
Note that when this is put in a batch file, '%' shall be doubled.
(for %%i in (a b "c d") do #echo %%~i)
If anybody comes here because they are looking to echo a blank line from a MINGW make makefile, I used
#cmd /c echo.
simply using echo. causes the dreaded process_begin: CreateProcess(NULL, echo., ...) failed. error message.
I hope this helps at least one other person out there :)
Ken and Jeb solutions works well.
But the new lines are generated with only an LF character and I need CRLF characters (Windows version).
To this, at the end of the script, I have converted LF to CRLF.
Example:
TYPE file.txt | FIND "" /V > file_win.txt
del file.txt
rename file_win.txt file.txt
If one needs to use famous \n in string literals that can be passed to a variable, may write a code like in the Hello.bat script below:
#echo off
set input=%1
if defined input (
set answer=Hi!\nWhy did you call me a %input%?
) else (
set answer=Hi!\nHow are you?\nWe are friends, you know?\nYou can call me by name.
)
setlocal enableDelayedExpansion
set newline=^
rem Two empty lines above are essential
echo %answer:\n=!newline!%
This way multiline output may by prepared in one place, even in other scritpt or external file, and printed in another.
The line break is held in newline variable. Its value must be substituted after the echo line is expanded so I use setlocal enableDelayedExpansion to enable exclamation signs which expand variables on execution. And the execution substitutes \n with newline contents (look for syntax at help set). We could of course use !newline! while setting the answer but \n is more convenient. It may be passed from outside (try Hello R2\nD2), where nobody knows the name of variable holding the line break (Yes, Hello C3!newline!P0 works the same way).
Above example may be refined to a subroutine or standalone batch, used like call:mlecho Hi\nI'm your comuter:
:mlecho
setlocal enableDelayedExpansion
set text=%*
set nl=^
echo %text:\n=!nl!%
goto:eof
Please note, that additional backslash won't prevent the script from parsing \n substring.
After a sleepless night and after reading all answers herein, after reading a lot of SS64 > CMD and after a lot of try & error I found:
The (almost) Ultimate Solution
TL;DR
... for early adopters.
Important!
Use a text editor for C&P that supports Unicode, e.g. Notepad++!
Set Newline Environment Variable ...
... in the Current CMD Session
Important!
Do not edit anything between '=' and '^'! (There's a character in between though you don't see it. Neither here nor in edit mode. C&P works here.)
:: Sets newline variables in the current CMD session
set \n=​^&echo:
set nl=​^&echo:
... for the Current User
Important!
Do not edit anything between (the second) '␣' and '^'! (There's a character in between though you don't see it. Neither here nor in edit mode. C&P works here.)
:: Sets newline variables for the current user [HKEY_CURRENT_USER\Environment]
setx \n ​^&echo:
setx nl ​^&echo:
... for the Local Machine
Important!
Do not edit anything between (the second) '␣' and '^'! (There's a character in between though you don't see it. Neither here nor in edit mode. C&P works here.)
:: Sets newline variables for the local machine [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment]
setx \n ​^&echo: /m
setx nl ​^&echo: /m
Why just almost?
It does not work with double-quotes that are not paired (opened and closed) in the same printed line, except if the only unpaired double-quote is the last character of the text, e.g.:
works: ""echo %\n%...after "newline". Before "newline"...%\n%...after "newline" (paired in each printed line)
works: echo %\n%...after newline. Before newline...%\n%...after newline" (the only unpaired double-quote is the last character)
doesn't work: echo "%\n%...after newline. Before newline...%\n%...after newline" (double-quotes are not paired in the same printed line)
Workaround for completely double-quoted texts (inspired by Windows batch: echo without new line):
set BEGIN_QUOTE=echo ^| set /p !="""
...
%BEGIN_QUOTE%
echo %\n%...after newline. Before newline...%\n%...after newline"
It works with completely single-quoted texts like:
echo '%\n%...after newline. Before newline...%\n%...after newline'
Added value: Escape Character
Note
There's a character after the '=' but you don't see it here but in edit mode. C&P works here.
:: Escape character - useful for color codes when 'echo'ing
:: See https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#text-formatting
set ESC=
For the colors see also https://imgur.com/a/EuNXEar and https://gist.github.com/gerib/f2562474e7ca0d3cda600366ee4b8a45.
2nd added value: Getting Unicode characters easily
A great page for getting 87,461 Unicode characters (AToW) by keyword(s): https://www.amp-what.com/.
The Reasons
The version in Ken's answer works apparently (I didn't try it), but is somehow...well...you see:
set NLM=^
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
The version derived from user2605194's and user287293's answer (without anything between '=' and '^'):
set nl=^&echo:
set \n=^&echo:
works partly but fails with the variable at the beginning of the line to be echoed:
> echo %\n%Hello%\n%World!
echo & echo:Hello & echo:World!
echo is ON.
Hello
World
due to the blank argument to the first echo.
All others are more or less invoking three echos explicitely.
I like short one-liners.
The Story Behind
To prevent set \n=^&echo: suggested in answers herein echoing blank (and such printing its status) I first remembered the Alt+255 user from the times when Novell was a widely used network and code pages like 437 and 850 were used. But 0d255/0xFF is ›Ÿ‹ (Latin Small Letter Y with diaeresis) in Unicode nowadays.
Then I remembered that there are more spaces in Unicode than the ordinary 0d32/0x20 but all of them are considered whitespaces and lead to the same behaviour as ›␣‹.
But there are even more: the zero width spaces and joiners which are not considered as whitespaces. The problem with them is, that you cannot C&P them since with their zero width there's nothing to select. So, I copied one that is close to one of them, the hair space (U+200A) which is right before the zero width space (U+200B) into Notepad++, opened its Hex-Editor plugin, found its bit representation E2 80 8A and changed it to E2 80 8B. Success! I had a non-whitespace character that's not visible in my \n environment variable.
To start a new line in batch, all you have to do is add "echo[", like so:
echo Hi!
echo[
echo Hello!
why not use substring/replace space to echo;?
set "_line=hello world"
echo\%_line: =&echo;%
Results:
hello
world
Or, replace \n to echo;
set "_line=hello\nworld"
echo\%_line:\n=&echo;%
For windows 10 with virtual terminal sequences there exists the means control the cursor position to a high degree.
To define the escape sequence 0x1b, the following can be used:
#Echo off
For /f %%a in ('echo prompt $E^| cmd')Do set \E=%%a
To output a single newline Between Strings:
<nul set /p "=Hello%\E%[EWorld"
To output n newlines where n is replaced with an integer:
<nul set /p "=%\E%[nE"
Many
Please note that all solutions that use cursor positioning according to Console Virtual Terminal Sequences, Cursor Positioning with:
Sequence
Code
Description
Behaviour
ESC [ <n> E
CNL
Cursor Next Line
Cursor down <n> lines from current position
only work as long as the bottom of the console window is not reached.
At the bottom there is no space left to move the cursor down so it just moves left (with the CR of CRLF) and the line printed before is overwritten from its beginning.
To echo a newline, add a dot . right after the echo:
echo.
This worked for me, no delayed expansion necessary:
#echo off
(
echo ^<html^>
echo ^<body^>
echo Hello
echo ^</body^>
echo ^</html^>
)
pause
It writes output like this:
<html>
<body>
Hello
</body>
</html>
Press any key to continue . . .
You can use #echo ( #echo + [space] + [insecable space] )
Note: The insecable space can be obtained with Alt+0160
Hope it helps :)
[edit] Hmm you're right, I needed it in a Makefile, it works perfectly in there. I guess my answer is not adapted for batch files... My bad.
simple
set nl=.
echo hello
echo%nl%
REM without space ^^^
echo World
Result:
hello
world
Be aware, this won't work in console because it'll simulate an escape key and clear the line.
Using this code, replace <ESC> with the 0x1b escape character or use this Pastebin link:
:: Replace <ESC> with the 0x1b escape character or copy from this Pastebin:
:: https://pastebin.com/xLWKTQZQ
echo Hello<ESC>[Eworld!
:: OR
set "\n=<ESC>[E"
echo Hello%\n%world!
Adding a variant to Ken's answer, that shows setting values for environment variables with new lines in them.
We use this method to append error conditions to a string in a VAR, then at the end of all the error checking output to a file as a summary of all the errors.
This is not complete code, just an example.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:: the two blank lines are required!
set NLM=^
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
:: Example Usage:
Set ErrMsg=Start Reporting:
:: some logic here finds an error condition and appends the error report
set ErrMsg=!ErrMsg!!NL!Error Title1!NL!Description!NL!Summary!NL!
:: some logic here finds another error condition and appends the error report
set ErrMsg=!ErrMsg!!NL!Error Title2!NL!Description!NL!Summary!NL!
:: some logic here finds another error condition and appends the error report
set ErrMsg=!ErrMsg!!NL!Error Title3!NL!Description!NL!Summary!NL!
echo %ErrMsg%
pause
echo %ErrMsg% > MyLogFile.log
Log and Screen output look like this...

Resources