1

Need help ! Working on a batch file which will replace a set of characters from a bunch of text files in a folder. I have found the code which will do that. But it does for only one file. IS there a way where it can do it for all the files in the folder. There a total of 1000 files inside the folder. I am using a Windows 7 OS. Attaching the code I found where it does for one file.

Thanks Harry


 @echo off
setlocal enabledelayedexpansion
set INTEXTFILE=Replace_string.txt

set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Apple
set REPLACETEXT=Mango
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
set OUTPUTLINE=

for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
   SET string=%%A
   for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
   if  "!string!" == "" (
       echo.>>%OUTTEXTFILE%
   ) else (
      SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
      echo !modified! >> %OUTTEXTFILE%
  )
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
  • use `FOR` in nested i.e. `for /f "delims=" %%# in ('dir /b /s "*mask.txt"') do ( for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %%~#"') do ( .... ) ... )` – Paul Nov 01 '15 at 01:38
  • Note that your inner `for /F` loop removes potential leading colons `:` from each line; to avoid that, use `"delims="`, then `set "string=%%a"`, and then `set "string=!string:*:=!"`; this removes the line number and the **first** `:` prepended by `forstring`, but nothing else... instead of `echo !modified!` you should write `echo.!modified!` to avoid `ECHO is Off` texts when a full line matches `!SEARCHTEXT!` and `REPLACETEXT` is empty... – aschipfl Nov 01 '15 at 09:53
  • 1
    @aschipfl: I suggest you to not use `echo.!modified!` for this purpose. You may use `echo/!modified!` or `echo(!modified!`. Further details at [this post](http://www.dostips.com/forum/viewtopic.php?f=3&t=774). – Aacini Nov 01 '15 at 13:08
  • That's very interesting, @Aacini! is it also the same for `rem.`? e. g., to create an empty file: `rem.>file.txt`? – aschipfl Nov 01 '15 at 15:16
  • 1
    @aschipfl: I didn't knew that! I used `rem > empty.txt` in old command.com to create an empty file, but that trick doesn't work in cmd.exe. I think this form: `rem/ > empty.txt` is better, because `rem.` access the disk in the same way than `echo.` – Aacini Nov 01 '15 at 19:33

2 Answers2

1

If you want a pure batch solution, then the simplest thing to do is to encapsulate the code in a subroutine that takes the name of the file as an argument, and call that routine from within a FOR loop that iterates the file names. This is a general approach that can be used for any code that you want to run iteratively against files in a folder.

The end result of your code does not create or rename any folders, so a simple FOR is safe to use. But if your code creates or renames folders, then a FOR loop could also process the newly created or renamed files. This can be solved by using FOR /F with the DIR /B command instead.

Note - I eliminated dead (unused) variables from the code.

@echo off
setlocal enabledelayedexpansion

pushd "c:\path\to\your\folder\containing\files\to\modify"
for %%F in (*.txt) do call :replace "%%F"
exit /b

:replace
set INTEXTFILE=%1
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
   SET string=%%A
   for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
   if  "!string!" == "" (
       echo.>>%OUTTEXTFILE%
   ) else (
      SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
      echo !modified! >> %OUTTEXTFILE%
  )
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
exit /b

But there are many limitations and inefficiencies with the code, with lots of room for improvement.

Limitations:

  • Input and output lines must be a bit less than 8191 bytes long.

  • The search ignores case

  • The search string cannot contain = or begin with ~, * or !

  • The replace string cannot contain !

  • Lines containing ! will be corrupted because delayed expansion is enabled when %%A is expanded. This can be solved by strategically toggling delayed expansion on and off within the loop(s).

  • Leading : will be stripped from all lines because consecutive delimiter characters are treated as a single delimiter.

  • The replacement will be corrupted if the search term contains %%a or %%b or %%A. This can be avoided by transferring the search and replacement terms to FOR variables.

  • Certain characters within the search and/or replacement terms could cause problems or require complex escape sequences. This can be simplified by getting the desired strings in environment variables (which may still require escape sequences) and then using delayed expansion and FOR /F to transfer the values to FOR variables.

  • There are obscure situations where ECHO. can fail. The only safe variant that is guaranteed to work is ECHO(.

  • A non empty line could become empty after replacement if the replacement string is empty, and the empty line will not be output properly because neither ECHO. nor ECHO( was used.

Inefficiencies / other issues

  • Redirection is performed for each line of output, which is slow. Better (faster) to redirect once outside the loop.

  • The DEL/RENAME pair can be replaced by a single MOVE command

  • CALL is relatively slow. Best to minimize use of CALL if you want the fastest possible solution. But sometimes it cannot be avoided.

  • I prefer to have my temp file name to be a derivative of the original name. I typically append a .new extension to the original name, so "original.txt" becomes "original.txt.new"

Below is highly optimized code that addresses all but the first 4 points above. It is about as robust and efficient as pure batch can get if you want to use FOR /F to read the file.

@echo off
setlocal disableDelayedExpansion

pushd "c:\path\to\your\folder\containing\files\to\modify"
set "find=Cat"
set "repl=Dog"

setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%S in ("!find!") do (
  for /f delims^=^ eol^= %%R in ("!repl!") do (
    endlocal
    for %%F in (*.txt) do (
      for /f "delims=" %%L in ('findstr /n "^" "%%F"') do (
        set "ln=%%L"
        setlocal enableDelayedExpansion
        set "ln=!ln:*:=!"
        if defined ln set "ln=!ln:%%S=%%R!"
        echo(!ln!
        endlocal
      )
    ) >"%%F.new" & move /y "%%F.new" "%%F" >nul
  )
)
popd

The above required a lot of experience and arcane knowledge to develop. Even after all the work, it still has the following limitations.

  • Input and output lines must be a bit less than 8191 bytes long.

  • The search ignores case

  • The search string cannot contain = or begin with ~, * or !

  • The replace string cannot contain !

Removing those limitations would require a ridiculous amount of slow and even more impenetrable code. Hardly worth it.

That is why I have abandoned using pure batch for text processing, and I developed JREPL.BAT to provide powerful regular expression text processing capabilities to the Windows command environment. JREPL is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.

JREPL makes the solution so simple, it can easily be run directly on the command line, without any batch file.

pushd "c:\your\path\with\files\to\be\modified"
for %F in (*.txt) do call jrepl "Cat" "Dog" /l /f "%F" /o -
popd

The search is case sensitive. You can add the /i option if you want the search to ignore case. Change the %F to %%F if you use the command within a batch script.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Thanks, it works just fine for all the files. It replaces one 'old string' with 'new string' But is there a way, the code can be tuned so as to replace multiple strings....in this case Apple =>Mango Cat=>Dog – Harry Puttar Nov 03 '15 at 21:09
  • 1
    I'm afraid you confused two limitations: the search string cannot **begin with** `!`, and it cannot **contain** `=`... – aschipfl May 06 '20 at 18:46
  • 1
    @aschipfl - Thanks. It was even worse than that. I've fixed the limitations in the answer, but I think I need to change the delayed [variable expansion rules](https://stackoverflow.com/a/7970912/1012053) a bit to be more like the percent expansion. Currently it implies that delayed expansion search can begin with `~`. – dbenham May 07 '20 at 02:34
  • 1
    You're welcome! Oh yes, I didn't recognise the `~`... But I think there's still something missing: the search string may contain `!` as long it's not the first char., isn't it? I'm quite sure it's the same as for `%`-expansion (whose behaviour I was [taught by you](https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#comment64274602_7970912), by the way... ;-)); or did I miss something else? – aschipfl May 07 '20 at 12:31
  • 1
    @aschipfl - Yes, thanks again! I even walked through that scenario in my head when I was reviewing the parsing rules, and still forgot to translate that limitation into the simple rules – dbenham May 07 '20 at 13:20
  • 1
    @aschipfl - I updated the delayed expansion parsing rules to disallow find string beginning with `~` – dbenham May 08 '20 at 01:05
0

Since all you need to do is replace text in an existing file, use a tool like FART which is specifically designed to do this:

FART -r "C:\Data\Directory\*.txt" "OldText" "NewText"

The -r switch says to process all txt files in provided directory and subfolders.

Note that you can add the -i switch to ignore the case when searching.

Jason Faulkner
  • 6,378
  • 2
  • 28
  • 33