2

i have to search a string from a txt like Pippo.K=5 and replace it with Pippo.K=1. I need to search the entire string. What i did is:

set "search=Pippo.K=5"
set "replace=Pippo.K=1"
set "textFile=%SettingFile%.txt"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
    set "line=%%i"
    setlocal enabledelayedexpansion
    set "line=!line:%search%=%replace%!"
    >>"%textFile%" echo(!line!
    endlocal
)

but what i returned is 5=Pippo.K=1=5

How can i fix this error?

aschipfl
  • 33,626
  • 12
  • 54
  • 99
Stella Bici
  • 31
  • 1
  • 5
  • Could you please provide some sample input data or an excerpt (by [editing](http://stackoverflow.com/posts/37724410/edit) your question post)? – aschipfl Jun 14 '16 at 23:43
  • Hey Stella, if any of the answers below was helpful, please consider marking one as accepted. [See this post](http://meta.stackexchange.com/q/5234/275822) for an explanation why this is important. – rojo Jun 25 '16 at 01:00

2 Answers2

4

The following script constitutes a pure solution. Supposing it is stored as repl-str.bat, you need to call it like this for your application:

repl-str.bat "%SettingFile%.txt" "Pippo.K=5" "Pippo.K=1" "%SettingFile%.txt"

This specifies the input file %SettingFile%.txt, the literal and case-sensitive search string Pippo.K=5, the replacement string Pippo.K=1 and the output file %SettingFile%.txt that is the same as the input file (the related technique has been taken from this answer: Batch script to find and replace a string in text file without creating an extra output file for storing the modified file). If no output file is given, the result is output to the console (useful for testing). If a fifth command line argument is given (arbitrary value), the search is done in a case-sensitive manner.

Here is the code of the script repl-str.bat:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "FILE_I=%~1"
set "SEARCH=%~2"
set "REPLAC=%~3"
set "FILE_O=%~4"
set "CASE=%~5"
set "FLAG=%~6"
if not defined FILE_I exit /B 1
if not defined SEARCH exit /B 1
if not defined FILE_O set "FILE_O=con"
if defined CASE set "CASE=#"
if defined FLAG set "FLAG=#"

for /F "delims=" %%L in ('
    findstr /N /R "^" "%FILE_I%" ^& break ^> "%FILE_O%"
') do (
    set "STRING=%%L"
    setlocal EnableDelayedExpansion
    set "STRING=!STRING:*:=!"
    call :REPL RETURN STRING SEARCH REPLAC "%CASE%" "%FLAG%"
    >> "%FILE_O%" echo(!RETURN!
    endlocal
)

endlocal
exit /B


:REPL  rtn_string  ref_string  ref_search  ref_replac  case  flag
setlocal EnableDelayedExpansion
set "STR=!%~2!"
set "SCH=!%~3!"
set "RPL=!%~4!"
if "%~5"=="" (set "OPT=/I") else (set "OPT=")
if not defined SCH endlocal & set "%~1=" & exit /B 1
set "SCH_CHR=!SCH:~,1!"
if not "%~6"=="" set "SCH_CHR="
if "!SCH_CHR!"=="=" set "SCH_CHR=" & rem = terminates search string
if "!SCH_CHR!"==""^" set "SCH_CHR=" & rem " could derange syntax
if "!SCH_CHR!"=="%%" set "SCH_CHR=" & rem % ends variable expansion
if "!SCH_CHR!"=="^!" set "SCH_CHR=" & rem ! ends variable expansion
call :LEN SCH_LEN SCH
call :LEN RPL_LEN RPL
set /A RED_LEN=SCH_LEN-1
set "RES="
:LOOP
call :LEN STR_LEN STR
if not defined STR goto :END
if defined SCH_CHR (
    set "WRK=!STR:*%SCH_CHR%=!"
    if %OPT% "!WRK!"=="!STR!" (
        set "RES=!RES!!STR!"
        set "STR="
    ) else (
        call :LEN WRK_LEN WRK
        set /A DFF_LEN=STR_LEN-WRK_LEN-1,INC_LEN=DFF_LEN+1,MOR_LEN=DFF_LEN+SCH_LEN
        for /F "tokens=1,2,3 delims=," %%M in ("!DFF_LEN!,!INC_LEN!,!MOR_LEN!") do (
            rem set "RES=!RES!!STR:~,%%M!"
            if defined WRK set "WRK=!WRK:~,%RED_LEN%!"
            if %OPT% "!STR:~%%M,1!!WRK!"=="!SCH!" (
                set "RES=!RES!!STR:~,%%M!!RPL!"
                set "STR=!STR:~%%O!"
            ) else (
                set "RES=!RES!!STR:~,%%N!"
                set "STR=!STR:~%%N!"
            )
        )
    )
) else (
    if %OPT% "!STR:~,%SCH_LEN%!"=="!SCH!" (
        set "RES=!RES!!RPL!"
        set "STR=!STR:~%SCH_LEN%!"
    ) else (
        set "RES=!RES!!STR:~,1!"
        set "STR=!STR:~1!"
    )
)
goto :LOOP
:END
if defined RES (
    for /F delims^=^ eol^= %%S in ("!RES!") do (
        endlocal
        set "%~1=%%S"
    )
) else endlocal & set "%~1="
exit /B


:LEN  rtn_length  ref_string
setlocal EnableDelayedExpansion
set "STR=!%~2!"
if not defined STR (set /A LEN=0) else (set /A LEN=1)
for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if defined STR (
        set "INT=!STR:~%%L!"
        if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!"
    )
)
endlocal & set "%~1=%LEN%"
exit /B

Basically, this approach takes the first character of the search string and looks it up in the input text. At each match, it is checked whether the whole search string occurs. If so, it is replaced by the replacement string by removing as many characters as the search string consists of, hence avoiding sub-string replacement syntax which fails in case the search string contains =, or the search or the replacement string contains % or !.
However, if the first character of the search string is =, ", % or !, the approach is different, the script checks every single character position for occurrence of the search string then, with the disadvantage of reduced overall performance. If a sixth command line argument is given (arbitrary value), this (slow) mode is forced.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Exactly, am in the situation of @Stella Bici. Am having the same problem when post-build action happened. How this can be resolved in post-build action of Visual Studio. Below is the sample code for your reference. set "search=Pippo.K=5" set "replace=Pippo.K=1" set "textFile=%SettingFile%.txt" for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do ( set "line=%%i" setlocal enabledelayedexpansion set "line=!line:%search%=%replace%!" >>"%textFile%" echo(!line! endlocal ) – Karthi Feb 27 '19 at 06:35
  • @Karthi, please do not comment every answer of this (old) question with a lot of code that is illegible; if you have a similar problem that you need a solution for, ask a new question; you may of course link this answer or the question there; thanks! – aschipfl Feb 27 '19 at 09:46
1

Batch variable substring substitution does have limitations. Dealing with literal equal signs is one of them.

powershell "(gc \"%textFile%\") -replace '%search%','%replace%'"

would work. That PowerShell one-liner is a simple alternative to your for /f loop without that limitation.


If you prefer a for /F loop, if your text file is an ini-style file, try this:

@echo off & setlocal

set "searchItem=Pippo.K"
set "searchVal=5"
set "newVal=1"
set "textFile=test.txt"

>"outfile.txt" (
    for /f "eol=; usebackq tokens=1* delims==" %%I in ("%textFile%") do (
        if /I "%%~I"=="%searchItem%" (
            if "%%~J"=="%searchVal%" (
                echo %%I=%newVal%
            ) else echo %%I=%%J
        ) else (
            if not "%%~J"=="" (echo %%I=%%J) else echo %%I
        )
    )
)
move /y "outfile.txt" "%textFile%"

Be advised that if any of the items in your file has a blank value (e.g. valuename=), the equal sign will be stripped unless you add some additional logic.

You might also consider using ini.bat from this answer.

Community
  • 1
  • 1
rojo
  • 24,000
  • 5
  • 55
  • 101
  • Exactly, am in the situation of @Stella Bici. Am having the same problem when post-build action happened. How this can be resolved in post-build action of Visual Studio. Below is the sample code for your reference. set "search=Pippo.K=5" set "replace=Pippo.K=1" set "textFile=%SettingFile%.txt" for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do ( set "line=%%i" setlocal enabledelayedexpansion set "line=!line:%search%=%replace%!" >>"%textFile%" echo(!line! endlocal ) – Karthi Feb 27 '19 at 06:31