Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
490 views
in Technique[技术] by (71.8m points)

preserve empty lines in a text file while using batch for /f

I have been trying to find and replace a string in a text file using batch script. I came across this answer which almost solved my issue, but the empty lines were not preserved in the output file. I have tried this answer too but lines start with line numbers []... [17]

Any help to to extend this answer to preserve empty lines in the output file would be appreciated. Thanks

    setlocal enableextensions disabledelayedexpansion

set "search=<Tool>"
set "replace=XYZ"

set "textFile=C:abc.txt"

for /f "delims=" %%i in ('type "%textFile%" ^| find /v /n "" ^& break ^> "%textFile%"') do (
    set "line=%%i"
    setlocal enabledelayedexpansion
    set "line=!line:%search%=%replace%!"
    >>"%textFile%" echo(!line!
    endlocal
)

The output looks like: enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You did not study the answer at your 2nd link hard enough - it has a solution that works perfectly well.

I prefer a variant of that technique that uses *]= replacement instead of substring:

@echo off
setlocal enableextensions disabledelayedexpansion

set "search=<Tool>"
set "replace=XYZ"

set "textFile=C:abc.txt"

for /f "delims=" %%i in ('type "%textFile%" ^| find /v /n "" ^& break ^> "%textFile%"') do (
    set "line=%%i"
    setlocal enabledelayedexpansion
    set "line=!line:*]=!"
    if defined line set "line=!line:%search%=%replace%!"
    >>"%textFile%" echo(!line!
    endlocal
)

But the code is not optimized - the append redirection slows things down because the output file must be opened and the file pointer positioned to the end-of-file for each iteration of the loop. It is much faster to redirect to a temporary file once, outside of the loop, and then use MOVE to replace the original with the temp.

I also prefer to use FINDSTR instead of FIND - it handles long lines better, and does not need a pipe or redirection.

@echo off
setlocal enableextensions disabledelayedexpansion

set "search=<Tool>"
set "replace=XYZ"

set "textFile=C:abc.txt"

>"%textFile%.new" (
  for /f "delims=" %%i in ('findstr /n "^" "%textFile%"') do (
      set "line=%%i"
      setlocal enabledelayedexpansion
      set "line=!line:*:=!"
      if defined line set "line=!line:%search%=%replace%!"
      echo(!line!
      endlocal
  )
)
move /y "%textFile%.new" "%textFile%" >nul

Truth be told, I never use pure batch to modify text files anymore. There are too many edge cases that take a lot of arcane code to work around. There are still many potential issues with the above code. For example:

  • The search string cannot contain =
  • The search string cannot begin with * or !
  • The replace string cannot contain !
  • The replacement can fail if the search and/or replace contain both " as well as poison characters like &, | etc.

I use the JREPL.BAT regular expression find/replace utility instead. It is faster, more robust, and much more powerful. It is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward, without any need for 3rd party exe files.

For example, the following simple command does your literal find/replace very quickly.

call jrepl "<Tool>" "XYZ" /l /f "C:abc.txt" /o -

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...