39

Is it possible to use a piped stdin stream inside a batch file?

I want to be able to redirect the output of one command into my batch file process.bat list so:

C:\>someOtherProgram.exe | process.bat

My first attempt looked like:

echo OFF
setlocal

:again
set /p inputLine=""
echo.%inputLine%
if not (%inputLine%)==() goto again

endlocal
:End

When I test it with type testFile.txt | process.bat it prints out the first line repeatedly.

Is there another way?

n611x007
  • 8,952
  • 8
  • 59
  • 102
m0tive
  • 2,796
  • 1
  • 22
  • 36

4 Answers4

37

set /p doesn't work with pipes, it takes one (randomly) line from the input.
But you can use more inside of an for-loop.

@echo off
setlocal
for /F "tokens=*" %%a in ('more') do (
  echo #%%a
)

But this fails with lines beginning with a semicolon (as the FOR-LOOP-standard of eol is ;).
And it can't read empty lines.
But with findstr you can solve this too, it prefix each line with the linenumber, so you never get empty lines.
And then the prefix is removed to the first colon.

@echo off
setlocal DisableDelayedExpansion

for /F "tokens=*" %%a in ('findstr /n "^"') do (
  set "line=%%a"
  setlocal EnableDelayedExpansion
  set "line=!line:*:=!"
  echo(!line!
  endlocal
)

Alternatively, on some environments (like WinRE) that don't include findstr, an alternative with find.exe might suffice. find will accept a null search string "", and allows search inversion. This would allow something like this:

@echo off
setlocal DisableDelayedExpansion

for /F "tokens=*" %%a in ('find /v ""') do (
  ...
jeb
  • 78,592
  • 17
  • 171
  • 225
  • 1
    This seems to work. I've not seen the `set "line=!line:*:=!"` syntax before. More batch script madness. I've adapted it slightly to use the MSYS tools; `sed "s/^\(.*\)$/\"\1\"/"'` instead of `findstr /n $`. The body can then be replaced with `echo.%%~a` and it can handle none dos line endings (which I have in alot of files). – m0tive Aug 08 '11 at 16:41
  • 6
    Better to use `FINDSTR /N "^"`. That search will correctly return all lines, including unix formatted lines as well as any final line that may not end with a newline. – dbenham Nov 13 '12 at 23:22
  • I think you can use `for /F "tokens=* eol="` to deal with lines beginning with a semicolon. – wangzq Nov 18 '12 at 01:15
  • 1
    Yes it can handle lines with semicolon, but then it fails with quotes, as EOL=" will not empty EOL – jeb Nov 18 '12 at 11:10
  • @jeb How about `for /F "tokens=* eol=`? – wizzwizz4 Nov 13 '17 at 18:29
  • 1
    @wizzwizz4 That doesn't remove the EOL, but it's possible, see [HOW TO: FOR /F Disabling EOF or using a quote as delim](http://www.dostips.com/forum/viewtopic.php?t=2385). But even then you can't fetch empty lines, therefore I use find or findstr – jeb Nov 13 '17 at 23:14
  • The 2nd example (`findstr`) is limited in line length. – bers Aug 25 '19 at 16:42
  • The same is true for the 3rd example (4096 is the limit here), and lines are cut off without warning. So better use the 2nd example, that one at least gives a warning :) – bers Aug 25 '19 at 17:11
  • 1
    @bers The third example is incomplete ((it's an edit from Dan). Batch has a hard limit of 8191 characzers for the line length and also for variables. – jeb Aug 25 '19 at 18:17
  • why not `for /F "tokens=1,* eol=: delims=:" %%A in ('findstr.exe /n "^"') do ( echo %%B ...` ? - It eliminates the `eol` bug/feature (because it's part of `delims`), doesn't ignore blank lines (`%%A` exists even for those thanks to `findstr /n`) and works with CRLF & LF (but not CR). – Limer Mar 08 '22 at 22:41
  • 1
    @Limer It fails for lines beginning with `:`, because they are consumed by the `delims=:`, too. I changed the `$` to `"^"` for better line end handling, like dbenham commented – jeb Mar 09 '22 at 06:40
3

The set "line=!line:*:=!" syntax is:

  1. set requires one parameter that is a=b.
    If a contains a space or something, you'll have to use the quotation marks around this parameter. Here I don't see any

  2. !line:*:=!
    For this syntax, you can type 'set /?' to see the official description on using variables.
    !var! is like %var%, to get the value. But !var! means delayed expansion.

line var name

the first : variable modification mark.

**:= **:=(empty), replace the string in the variable's value matches "*:"(virtually from the string start to first : occurence) with (empty), i.e. delete the substring from start to first colon.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
YumeYao
  • 557
  • 4
  • 10
1
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
    >  CON    ECHO.%%B
    >> %File% ECHO.%%B
)

Source here: http://www.robvanderwoude.com/unixports.php#TEE

Amr Ali
  • 3,020
  • 1
  • 16
  • 11
0

Alternatively, on some environments (like WinRE) that don't include findstr, an alternative with find.exe might suffice. find will accept a null search string "", and allows search inversion. This would allow something like this:

@echo off
setlocal DisableDelayedExpansion

for /F "tokens=*" %%a in ('find /v ""') do (
  set "line=%%a"
  echo(!line!
)
Dan
  • 273
  • 3
  • 6