32

Long commands in Windows batch files can be split to multiple lines by using the ^ as mentioned in Long commands split over multiple lines in Windows Vista batch (.bat) file.

However, if the caret is inside a double quoted string, it won't work. For example:

echo "A very long line I want to ^
split into two lines"

This will print "A very long line I want to ^ and tell me split is an unknown command.

Is there a way to get around this?

Mofi
  • 46,139
  • 17
  • 80
  • 143
Wang Dingwei
  • 4,661
  • 6
  • 32
  • 43

5 Answers5

28

I see three possible workarounds.

1) Building the line combining multiple for-parameters.

@echo off
SETLOCAL EnableDelayedExpansion

set "line="
for %%a in ("line1" 
"line2"
"line3"
"line4"
) do set line=!line!%%~a
echo !line!

Drawback: It drops lines, when there is a ? or * in the text.

2) Leaving the "quote" at the end of each line

@echo on
SETLOCAL EnableDelayedExpansion

set "line=line1 & x#"^
 "line2 & a#"^
 "line3 & b #"^
 "line4 & c "

set "line=!line:#" "=!"
echo !line!

The first space in each line is important, because the caret works as multiline character but it also escapes the first character, so also a quote would be escaped.
So I replace the unnessary #" " after building the line.

EDIT Added: 3) Disappearing quotes

setlocal EnableDelayedExpansion
echo "A very long line I want to !="!^
split into two lines"

In my opinion this is the best way, it works as the parser first see the quotes and therefore the last caret will work, as it seems to be outside of the quotes.
But this !="! expression will expand the variable named =", but such a variable name can't exists (an equal sign can't occur as first character) so it expands to nothing.

You can also create safe expressions, they will always escape out of quotes, independent if there is a quote or not in the line.
!="^"!

echo This multiline works !="^"!^
as expected
echo "This multiline works !="^"!^
too"

If you want avoid delayed expansion, you could also use a -FOR-Loop like

for %%^" in ("") do (
echo "This multiline works %%~"^
too"
)
jeb
  • 78,592
  • 17
  • 171
  • 225
  • The answer isn't simple, but these are some cool tricks! I wish I could have multiple upvotes. Thanks! – Wang Dingwei Jan 10 '11 at 14:45
  • I really really like method #1. Are there any gotcha's on that method? – djangofan Sep 14 '12 at 00:18
  • It fails with characters like `*?` and, as the for loop try to use them as wildcards. But then you can switch to a FOR /F loop – jeb Sep 14 '12 at 07:31
  • First method worked for me, I just removed the quotes around the variable on line 4 because I was getting a message that the environment variable doesn't exist. After that, it worked great. – midoriha_senpai Jan 18 '18 at 19:19
13

The most straight forward answer is to escape the quotes. They will be printed, but they will not functionally quote the contents as far as CMD.EXE is concerned.

@echo off
echo ^"A very long line I want to ^
split into two lines^"

Any special characters that appear in the line must also be escaped since they are no longer functionally quoted.

@echo off
echo ^"A very long line I want to ^
split into two lines ^
that also contains special characters ^^ ^& ^| ^> ^< ^"

As jeb said, the line continuation escapes the first character of the next line. So if the first character of the next line happens to be a special character, it should not be escaped a second time. The code below will successfully escape the & while it also introduces a new line.

@echo off
echo ^"A very long line I want to ^
split into two lines ^
that also contains special characters ^^ ^
& ^| ^> ^< ^"
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • 1
    +1, but I prefere disappearing quotes, as you don't need to escape the special characters – jeb Apr 12 '12 at 20:26
  • I like the disappearing quotes better as well :-) – dbenham Apr 12 '12 at 20:43
  • As long as you know caret is the escape character, I think this technique is much easier to remember. No need to understand/enable delayed expansion, nor understand/memorize a delayed expansion *hack*. But to each his own. – G-Wiz Oct 22 '13 at 02:29
  • this is better, clear and easier than the accepted answer. you are always the best. – Badr Elmers Apr 07 '20 at 06:54
  • Is there a way to create an empty line? – Bernhard Döbler Aug 26 '20 at 19:02
  • @BernhardDöbler - See [Explain how Windows batch newline variable hack works](https://stackoverflow.com/q/6379619/1012053) – dbenham Aug 29 '20 at 19:27
4

I came up with my own method for this today, and I've never seen it suggested anywhere so I thought I'd post it here. Certainly, it's not elegant in an objective sense. I would say it has it's own unique kind of ugly and some limitations, but I find it far more "ergonomic" when compared to the alternatives.

Specifically, this method has the unique characteristic of not needing to escape anything, while still performing typical variable substitution. This enables me to to take some string that looks exactly how I want it, spread it across multiple lines, and add a prefix to each line without changing anything else in the string. Thus, there's no trial-and-error needed to figure out how special characters and escape characters might interact. Overall, I think it reduces the cognitive load needed to deterministically produce a complicated string variable over multiple lines, which is good for me because I don't want to have to think hard and get frustrated with incidental complexity like the nuances of the windows command interpreter. Also, a similar technique can be used on linux bash, making it somewhat portable.

Note: I've not thoroughly tested it, it might not work for some use cases, I don't know. However, the example has a fair number of seemingly troublesome characters an it works, so it seems somewhat robust.

set IMAGE_NAME=android-arm64
set CMD=docker run --rm
set CMD=%CMD% --platform=linux
set CMD=%CMD% -v %CD%:/work
set CMD=%CMD% dockcross/%IMAGE_NAME%
set CMD=%CMD% /bin/sh -c
set CMD=%CMD% "mkdir build-%IMAGE_NAME% &&
set CMD=%CMD% cd build-%IMAGE_NAME% &&
set CMD=%CMD% cmake .. &&
set CMD=%CMD% cmake --build ."

echo %CMD%

In my case, it's a command, so I can run it with:

%CMD%

I'd be open to any feedback or suggestions about this method. Perhaps it can even be improved.

solvingJ
  • 1,321
  • 1
  • 19
  • 30
2

The clear advantage of @solvingJ's method over the variants of Jeb is that it is quite obvious what happens and that there is a spatial seperation of on the left hand the ugly cmd stuff and on the right the actual content. And it can be improved in readability by putting more spaces between the left and the right part:

set      CMD=   docker run --rm
set CMD=%CMD%     --platform=linux
set CMD=%CMD%     -v %CD%:/work
set CMD=%CMD%     dockcross/%IMAGE_NAME%
set CMD=%CMD%     /bin/sh -c
set CMD=%CMD%       "mkdir build-%IMAGE_NAME% &&
set CMD=%CMD%       cd build-%IMAGE_NAME% &&
set CMD=%CMD%       cmake .. &&
set CMD=%CMD%       cmake --build ."

Unfortunately it is not possible to demonstrate this in a comment because multiple spaces are reduced to one there. So I do in an answer what is actually just a comment to @solvingJ's answer, which I consider the best.

TNT
  • 3,392
  • 1
  • 24
  • 27
-3

If you want to add blank lines then ensure you have a space BEFORE the ^ on the 2nd line

echo Hello world^ ^ Have a good day !

Colin Hay
  • 25
  • 3
  • -1: in [quoted mode](https://stackoverflow.com/a/4095133/12861751)(***UNESCAPED*** caret `^`) ALL characters except `\n` lose their meaning, so this will not work: `echo "quotes break this trick^next line` – ScriptKidd Jun 02 '20 at 14:36