4

I have a batch script which replaces several occurrences of a source string within a source text file with a user input string.
For this I use a slightly corrected version of this answer here: How to replace substrings in windows batch file
(This is not the accepted answer, the accepted one does alter the source file even more!) But I was only able to resolve a little "adds a space to every line"-bug, not the major problem of not being able to use source files with exclamation marks within!

For this remaining problem I researched some more and e.g. found (and tried the suggestions of) these topics:
- exclamation point being removed constantly in batch file
- Windows batch - Delayed expansion removes exclamation mark
Sadly these questions' solutions (of temporarily disabling the "delayed expansion") do not apply for me since there is a single line within my script which does need the "delayed expansion" AND has to process variable values with potential exclamation marks!

This is the line
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
which can be seen in its context following my first link.
As a short explanation: This line needs the delayed expansion to be functional since it resides within an if-else-block within a for-block. On the other hand the value of variable 'string' could contain exclamation marks since it scans through the source file, line by line.

Does anybody has an idea how I could resolve this?

EDIT:
According to the first comments I tried to enable the delayed expansions only for the necessary parts of the main loop (only where access to modified variable content is needed, using the "!"-operator). The whole main loop now looks like this:


    setlocal disabledelayedexpansion
    for /f "tokens=1,* delims=¶" %%A in ('"findstr /n ^^ %INTEXTFILE%"') do (
      SET string=%%A
      setlocal enabledelayedexpansion
      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%
      )
      endlocal
    )
    endlocal

Sadly this yields no benefit at all. This results in the same output file as when I had enabled the delayed expansions for the whole script.
Did I apply the given hints in a wrong way? (If I restrict the enabled range any further, I only get empty lines or other rubbish in the output.)

Community
  • 1
  • 1
  • Not a direct answer, but I think the simplest one-line solution would be using [JREPL](http://www.dostips.com/forum/viewtopic.php?f=3&t=6044). – wOxxOm Oct 22 '15 at 15:47
  • One more topic to try [Missing “!” string when EnableDelayedExpansion](http://stackoverflow.com/q/33257824/2861476) – MC ND Oct 22 '15 at 15:59
  • 2
    The problem is not such line, but the `SET string=%%A` one, and the solution was posted one answer _below_ your posted one (eichhorn's one) and _also_ one answer below that (AndriyM's one)... – Aacini Oct 22 '15 at 17:39
  • @ MCND and @Aacini: I tried your suggestions but failed in fixing the problem. I edited my question accordingly. Would you please look into my current implementation? – user3471872 Oct 23 '15 at 09:35

1 Answers1

1

There are multiple bugs in your edited code.

You are using findstr/n, this prefixes each line with a line number and a colon.
But you should avoid the delim set to , as even it's a uncommon character, it's still a possible character.

Later you try to remove the line number and the colon with

for /f "delims=: tokens=1,*" %%A in ("!string!") do set "string=%%B"

But this will also remove all leading colons of your input file and destroys also all exclamation marks and sometimes also carets are affected.

Better use

set "string=!string:*:=!" Remove all up to the first colon

Then both of your echo lines are bad.
echo. is slow and can fail.
echo !variable! fails when the wrong content is in variable

So we end up with

setlocal disabledelayedexpansion
for /f "tokens=* delims=" %%A in ('"findstr /n ^^ %INTEXTFILE%"') do (
  SET "string=%%A"
  setlocal EnableDelayedExpansion
  set "string=!string:*:=!"
  if defined string (
    SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
  )
    (echo(!modified!) >> %OUTTEXTFILE%
  endlocal
)
endlocal

The echo line looks a bit odd, but it's more or less simple.
First I enclose a normal echo into a parenthesis block, to ensure that I don't add unwanted spaces to the file, therefor I can add as many spaces after the closing parenthesis, as I want.

(echo Sample)    >> myOutputFile.txt

But as we need to expand a variable with unknown content, a normal ECHO can't be used here, see this example.

set "var1=/?"
set "var2=    "
set "var3=ON"
echo %var1%
echo %var2%
echo %var3%

But echo( works safe with any content. It looks strange but currently there are no side known.

So I simply enclose echo(!variable! into parenthesis (echo(!variable!), where only the outer parenthesis build the block, the second opening parenthesis is part of the echo command.

jeb
  • 78,592
  • 17
  • 171
  • 225
  • Thanks for your very useful answer and good explanations so far. Your solution works just like it should. - But could you please elaborate your use of the echo command? I do not know the syntax you are using; I cannot find it within the Microsoft documentation. E.g. what does the extra left parenthesis mean; what are the parentheses around the `!modified!` (and the left out space after `echo`) for (Is this some kind of function call?); why can I use a space character in front of the `>>` in this case without altering the output? – user3471872 Oct 23 '15 at 11:19