6

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

Community
  • 1
  • 1

1 Answers1

9

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 -
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Thank you for the answer and explanation. It worked :) I also tried FINDSTR instead of FIND, I was getting some strange output.. But, your solution worked as expected..:) –  Aug 02 '16 at 16:54
  • Hi @dbenham, can you please tell what does echo(!line! do? I mean what is use of '(' in this? – atul_pant Jun 07 '21 at 12:02
  • @atul_pant - There are a number of edge cases where `echo !line!` and other more frequently seen forms can fail - meaning something is printed other than the content of the variable. Via experimentation by a number of people, it has been determined that `echo(` is the only form that is guaranteed to work regardless what follows. I know it is ugly, but it works. – dbenham Jun 07 '21 at 21:26
  • Thanks. Is there any documentation which tells that what exactly '(' do after echo? Just like we use ! around variable name if delayed expansion is enabled, is there similar meaning of '(' when used with echo, like it is done above? – atul_pant Jun 08 '21 at 07:46