Echoing Multiple Lines into a Pipe

If you’ve been batch scripting on Windows for a while, you probably know the way to echo a blank line: a period with no space in front of it.


And the way to chain commands together:

  • A || B runs B if A failed
  • A && B runs B if A succeeded
  • A & B runs B regardless

It may not be obvious, but you can echo multiple lines into a pipe without resorting to a temp file.

(echo def& echo abc) | sort

You will have to butt the ampersand against the end of each echo, or you’ll be sending spaces at the end of each line.

If you make a lot of lines, mashing them together gets hard to read. The solution to that obscure situation is below.
Edit (2010-04-25): Argh! Ampersand butting works for redirecting to a file, and FOR loops (as you’ll see in a later post), but plain ol’ pipes still get trailing spaces!
Example: Pipe into sort, get spaces. Dump to tmp.txt, no spaces.
My “Right Way”, below the fold, somehow only got a space on the final line.

The Broken Way

Finding the magic syntax at the end of the post was tricky. Forgive the plain code sections; had to avoid justified word spacing.

My first attempt was an equally tricky variable that contained a newline to concatenate. Did not work.

REM Filling %N% with a newline (\r\n aka <0x0D,0x0A>)
REM The above two lines must be blank

* When you copy/paste, remove the lone spaces on empty lines.

  • Cmd.exe’s escape char is “^”. That will be important later.
  • Technically NLM holds a genuine newline, which would be like hitting return halfway through writing a command (bad).
  • N actually contains “^{0x0D,0x0A}{0x0D,0x0A}” aka an escape and two genuine newlines.
    (after Cmd interprets that SET).
    As crazy as it looks, that’s the safe sequence of chars to have in a cmd line, for one round of interpretation at least.
  • If you type ^{enter}{enter} at a real live prompt in the middle of writing an echo, you’ll see a confusing “More?” message, but when you finish the command, you’ll get a newline in there.


ECHO def%N%abc| sort
REM Gets interpreted to pipe "def\r\nabc" into sort.
REM This is fine, so long as Cmd never sees that N again.

REM In other words, don't use N to set another variable!
REM SET zzz=abc%N%def
REM ECHO %zzz%
REM When zzz gets parsed, it's like hitting return after 'c'.

REM So this is all kinds of broken...
SET msg=This is the first line.
SET msg=%msg%%N%This is the second line.
SET msg=%msg%%N%This is the third line.
SET msg=%msg%%N%This is the fourth line.
ECHO %msg%|sort

Unlike most other languages, every time you use a variable in batch, its contents get interpreted again. And if you try to concatenate, the oldest segments will be interpreted more times than the youngest bits. Madness.

The Right Way

Now the way that really works.

(ECHO This is the first line.^
& ECHO This is the second line.^
& ECHO This is the third line.^
& ECHO Your name is %username%.^
& ECHO This line has ^^^(some^^^) special ^^^> characters.^
& ECHO This is the sixth line.) | sort

Each line ends with a caret, which escapes the newline. It looks simple, but if you stray from that arrangement, Cmd will silently die without giving any hints.
Cmd seems to be interpreting these lines twice-over, which is why I had to escape the caret.

  • Original: “^^^(“
  • Pass one: “^(“
  • Pass two: “(“

When you need to do this? It comes in handy when you need to echo source code of some other language into an interpreter (sqlcmd/osql, spidermonkey), and you’re generating the text on-the-fly to substitute batch arguments and variables.

Or to feed netcat as it spoofs a text protocol client (POP, HTTP, etc), though you’ll want to set netcat’s line-by-line delay to give remote servers time to chat back.


3 Responses to “Echoing Multiple Lines into a Pipe”

  1. Jojo says:

    I don’t understand why you did it this way. You need neither the &’s nor the ^’s, just do:

    (ECHO This is the first line.
    ECHO This is the second line.
    ECHO This is the third line.
    ECHO And so on.) | sort

  2. jeb says:

    Nice, but still you have the problem of the space in the last line.

    And why this doesn’t work (it appends spaces to each line)?

    ( echo Line1& echo Line2& echo Line3) | sort

    But this works!?! (But still the problem for the space after line3)
    ( echo Line1^& echo Line2^& echo Line3) | sort

    Or to avoid even the last space by using a new lines…
    SET LF=^

    REM ** The two empts lines are required for the new line **

    echo line1%%LF%%rem.
    echo line2%%LF%%rem.
    echo line3%%LF%%rem.
    ) | sort

    Btw. The difference between pipes and redirection is large.

    Redirection works as the code block will be executed as it.

    But when you use a pipe the complete block will be *folded* and then executed in a new cmd.exe context and therefore carets and percents will be parsed twice

  3. Vhati says:

    Your example results in a space being added at the end of every line.

    Well done. :)

Leave a Reply