9

This DOS batch script is stripping out the blank lines and not showing the blank lines in the file even though I am using the TYPE.exe command to convert the file to make sure the file is ASCII so that the FIND command is compatible with the file. Can anyone tell me how to make this script include blank lines?

@ECHO off
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE.exe "build.properties" ^| FIND.exe /V ""`) DO (
  ECHO --%%A--
)
pause
djangofan
  • 25,461
  • 54
  • 171
  • 262

5 Answers5

19

That is the designed behavior of FOR /F - it never returns blank lines. The work around is to use FIND or FINDSTR to prefix the line with the line number. If you can guarantee no lines start with the line number delimiter, then you simply set the appropriate delimiter and keep tokens 1* but use only the 2nd token.

::preserve blank lines using FIND, assume no line starts with ]
::long lines are truncated
for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B

::preserve blank lines using FINDSTR, assume no line starts with :
::long lines > 8191 bytes are lost
for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B

::FINDSTR variant that preserves long lines
type "file.txt" > "file.txt.tmp"
for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
del "file.txt.tmp"

I prefer FINDSTR - it is more reliable. For example, FIND can truncate long lines - FINDSTR does not as long as it reads directly from a file. FINDSTR does drop long lines when reading from stdin via pipe or redirection.

If the file may contain lines that start with the delimiter, then you need to preserve the entire line with the line number prefix, and then use search and replace to remove the line prefix. You probably want delayed expansion off when transferring the %%A to an environment variable, otherwise any ! will be corrupted. But later within the loop you need delayed expansion to do the search and replace.

::preserve blank lines using FIND, even if a line may start with ]
::long lines are truncated
for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
  set "ln=%%A"
  setlocal enableDelayedExpansion
  set "ln=!ln:*]=!"
  echo(!ln!
  endlocal
)

::preserve blank lines using FINDSTR, even if a line may start with :
::long lines >8191 bytes are truncated
for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
  set "ln=%%A"
  setlocal enableDelayedExpansion
  set "ln=!ln:*:=!"
  echo(!ln!
  endlocal
)

::FINDSTR variant that preserves long lines
type "file.txt" >"file.txt.tmp"
for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
  set "ln=%%A"
  setlocal enableDelayedExpansion
  set "ln=!ln:*:=!"
  echo(!ln!
  endlocal
)
del "file.txt.tmp"

If you don't need to worry about converting the file to ASCII, then it is more efficient to drop the pipe and let FIND or FINDSTR open the file specified as an argument, or via redirection.

There is another work around that completely bypasses FOR /F during the read process. It looks odd, but it is more efficient. There are no restrictions with using delayed expansion, but unfortunately it has other limitations.

1) lines must be terminated by <CR><LF> (this will not be a problem if you do the TYPE file conversion)

2) lines must be <= 1021 bytes long (disregarding the <CR><LF>)

3) any trailing control characters are stripped from each line.

4) it must read from a file - you can't use a pipe. So in your case you will need to use a temp file to do your to ASCII conversion.

setlocal enableDelayedExpansion
type "file.txt">"file.txt.tmp"
for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
<"file.txt.tmp" (
  for /l %%N in (1 1 %cnt%) do(
    set "ln="
    set /p "ln="
    echo(!ln!
  )
)
del "file.txt.tmp"
dbenham
  • 119,153
  • 25
  • 226
  • 353
  • 1
    No problem there, but I agree it looks bad. It is a more reliable option than `ECHO.`. There are obscure situations where `ECHO.` can fail, whereas `ECHO(` never fails. The point of either syntax is to allow output of a blank line. – dbenham Jan 11 '12 at 00:29
  • 1
    Can you explain the line "echo(!ln!" ? Isn't there a problem with a closing parenthesis? Also, the line above it is missing the closing ! char? – djangofan Jan 11 '12 at 00:31
  • Finally got it working. Your knowledge in this area is awesome. thanks. – djangofan Jan 11 '12 at 00:35
  • @Didzis/Tom/X.L.Ant - Unless you know why [I am seeing results that suggest that the statement by this user that "{in all versions of windows} skipping blank lines is a designed feature" is incorrect](http://stackoverflow.com/a/14642856/1126551), am not sure why my suggested edit correcting this was rejected?? – user66001 Feb 01 '13 at 09:30
  • @dbenham , is there any reason that you need to use the `TYPE` command, then pipe it into the `FIND` command? Why not halve the file access and just use `FOR`'s `skip` command with the `FIND` command like so: `FOR /f "tokens=1* skip=2 delims=]" %%x in ('FIND x.txt /n /v ""') DO @IF "%y"=="" (ECHO.) else (ECHO %%y)` That way the data is only read from the disk once. – James K Feb 03 '13 at 01:51
  • @JamesK - I use TYPE to make sure the content is ASCII. Re-read the OP's question and then read the 4th paragraph in my answer (disregarding code). I address your concerns there. – dbenham Feb 03 '13 at 02:04
  • EDIT - Corrected answer to address FINDSTR with redirected or piped input dropping long lines, and added a variant that preserves long lines. – dbenham Feb 09 '15 at 13:13
  • Is there a workaround if lines are longer than 1021 bytes? – JCarlosR Oct 23 '19 at 19:04
  • 1
    @JCarlos - No, the SET /P technique has a hard 1021 byte limit. – dbenham Oct 23 '19 at 19:51
2

I wrote a very simple program that may serve as replacement for FIND and FINDSTR commands when they are used for this purpose. My program is called PIPE.COM and it just insert a blank space in empty lines, so all the lines may be directly processed by FOR command with no further adjustments (as long as the inserted space don't cares). Here it is:

@ECHO off
if not exist pipe.com call :DefinePipe
FOR /F "USEBACKQ delims=" %%A IN (`pipe ^< "build.properties"`) DO (
  ECHO(--%%A--
)
pause
goto :EOF

:DefinePipe
setlocal DisableDelayedExpansion
set pipe=´)€ì!Í!ŠÐŠà€Ä!€ü.t2€ü+u!:æu8²A€ê!´#€ì!Í!².€ê!´#€ì!Í!²+€ê!´#€ì!Í!Šò€Æ!´,€ì!Í!"Àu°´LÍ!ëÒ
setlocal EnableDelayedExpansion
echo !pipe!>pipe.com
exit /B

EDIT: Addendum as answer to new comment

The code at :DefinePipe subroutine create a 88 bytes program called pipe.com, that basically do a process equivalent to this pseudo-Batch code:

set "space= "
set line=
:nextChar
   rem Read just ONE character
   set /PC char=
   if %char% neq %NewLine% (
      rem Join new char to current line
      set line=%line%%char%
   ) else (
      rem End of line detected
      if defined line (
         rem Show current line
         echo %line%
         set line=
      ) else (
         rem Empty line: change it by one space
         echo %space%
      )
   )
goto nextChar

This way, empty lines in the input file are changed by lines with one space, so FOR /F command not longer omit they. This works "as long as the inserted space don't cares" as I said in my answer.

Note that the pipe.com program does not work in 64-bits Windows versions.

Antonio

Aacini
  • 59,374
  • 12
  • 63
  • 94
  • Thanks for the help also from dos tips – djangofan Jan 12 '12 at 18:43
  • Excuse me. Could you confirm if my PIPE.COM program solved your problem? – Aacini Jan 12 '12 at 20:03
  • @Aacini - Could you describe how this solution works in more detail? – user66001 Feb 04 '13 at 04:21
  • @Aacini - Thanks. Though I don't follow all of it (I do believe I realise now that `%pipe%` contains the compiled code of a executable program, though). If you wouldn't mind, could you explain: what the C does in `set /PC char=` and how ¿this line takes only one char?; where the contents of `%NewLine%` is defined; that the `echo`'s don't join the character/space and a trailing newline onto `%line%`; and lastly what variable is what all individual chars are joined onto one by one in each ¿second? iteration. Also, can a 64bit version not be created? – user66001 Feb 05 '13 at 08:49
  • @user66001: As I said in my answer, this is pseudo-Batch code, **NOT** real code. It is just a way to explain you what pipe.com does instead of: "Read next character, if is not NL append to current line, else show current line, but if it is empty, insert a space". This process can NOT be achieved by a Batch file precisely because the details you mention. The original pipe.asm program was written in assembly language. – Aacini Feb 05 '13 at 18:58
  • @Aacini - Okay; Sorry; Thanks. – user66001 Feb 06 '13 at 04:31
1

Output lines including blank lines

Here's a method I developed for my own use.

Save the code as a batch file say, SHOWALL.BAT and pass the source file as a command line parameter.

Output can be redirected or piped.

@echo off

for /f "tokens=1,* delims=]" %%a in ('find /n /v "" ^< "%~1"') do echo.%%ba

exit /b

EXAMPLES:

showall source.txt

showall source.txt >destination.txt

showall source.txt | FIND "string"

An oddity is the inclusion of the '^<' (redirection) as opposed to just doing the following:

for /f "tokens=1,* delims=]" %%a in ('find /n /v "" "%~1"') do echo.%%ba

By omitting the redirection, a leading blank line is output.

Paul Tomasi
  • 649
  • 3
  • 9
  • 1
    Nice, but it strips leading `]` characters from the lines, and `echo.` will fail if a line exists like `\..\..\Windows\system32\calc.exe` – jeb Jan 14 '12 at 07:29
  • 1
    @jeb , Actually, on Windows 7 at least, it will not strip all the `]` characters. Just the first one, or up until it meets the `*`. – James K Feb 03 '13 at 02:08
0

As mentioned in this answer to the above question, it doesn't seem that lines are skipped by default using for /f in (at least) Windows XP (Community - Please update this answer by testing the below batch commands on your version & service pack of Windows).


   EDIT: Per Jeb's comment below, it seems that the ping command, in at least Windows XP, is
   causing for /f to produce <CR>'s instead of blank lines (If someone knows specifically why, would
   appreciate it if they could update this answer or comment).

   As a workaround, it seems that the second default delimited token (<space> / %%b in the example)
   returns as blank, which worked for my situation of eliminating the blank lines by way of an "parent"
   if conditional on the second token at the start of the for /f, like this:

   for /f "tokens=1,2*" %%a in ('ping -n 1 google.com') do (
      if not "x%%b"=="x" (
         {do things with non-blank lines}
      )
   )



Using the below code:

@echo off
systeminfo | findstr /b /c:"OS Name" /c:"OS Version" 
echo.&echo.
ping -n 1 google.com
echo.&echo.
for /f %%a in ('ping -n 1 google.com') do ( echo "%%a" )
echo.&echo.&echo --------------&echo.&echo.
find /?
echo.&echo.
for /f %%a in ('find /?') do ( echo "%%a" )
echo.&echo.
pause

.... the following is what I see on Windows XP, Windows 7 and Windows 2008, being the only three versions & service packs of Windows I have ready access to:

Windows XP Pro SP3

Windows 7 Enterprise SP1

Windows Server 2008 R2 Enterprise SP1

Community
  • 1
  • 1
user66001
  • 696
  • 1
  • 12
  • 34
  • Like dbenham said: For /f strips always blank lines. In your case it seems that XP doesn't strip blank lines, but in reallity you get a line with only a single quote between `"Pinging"` and `"Reply"`. But this doesn't mean that FOR/F fetches an empty line. The line contains a `` character, therefore the cursor jumps back to the line beginning and the first quote is overwritten by the last quote, that's all. The ping command under XP is simply buggy – jeb Feb 01 '13 at 09:36
  • Thanks @jeb. I came to this page due to the problem with (what I thought at the time was) extra spaces, but realise now (corrected by you) they are only carriage returns being output on XP. Can you also perhaps advise how one would test for the `` in a `if not "x%%a"=""` statement? – user66001 Feb 01 '13 at 10:06
  • To remove `CR` you can call a function like `:trimmer set "trim=%1"` this works, as all `CR` are removed after the percent expansion phase. But for more questions, you should open an own question – jeb Feb 01 '13 at 11:52
  • Your not using the '~' character properly. It removes quotes on outside of strings for you. Understanding this might be the root of your problem. Instead of '%%a' you should use '%%~a' . – djangofan Feb 02 '13 at 05:46
  • @djangofan Didn't understand at first, I wasn't using ~ at all, let alone properly. However, after inserting it as shown in [this screenshot](http://i.imgur.com/iXw2aJX.png) I must be doing it wrong. The issue is, as I believe jeb has correctly identified above, that `for /f` is not completely ignoring blank lines, I guess it is removing the `lf`, but not the `cr` on Win XP. I am just encasing it in quotes to see what it is outputting. – user66001 Feb 02 '13 at 09:25
0

Thanks to dbenham, this works, although it is slightly different than his suggestion:

::preserve blank lines using FIND, no limitations
for /f "USEBACKQ delims=" %%A in (`type "file.properties" ^| find /V /N ""`) do (
  set "ln=%%A"
  setlocal enableDelayedExpansion
  set "ln=!ln:*]=!"
  echo(!ln!
  endlocal
)
djangofan
  • 25,461
  • 54
  • 171
  • 262
  • 1
    What isn't working? What are you seeing? I see one problem. `ECHO !ln!` will print `ECHO is off` if the line is blank! That is why I used `ECHO(!ln!` – dbenham Jan 11 '12 at 00:32
  • Ok, i updated it. It works now. thank you very much! Now I just need to integrate this into my project. – djangofan Jan 11 '12 at 00:33