-2

I have a problem with the code ...
I have the files t1.txt and t2.txt. The goal is to move from file 1 to file 2 with the same content unless it contains a specific word, in which case, the entire line must be replaced by a predefined one.

specifically I have problems with the 'if' and with the reassignment of the variable.

@echo off
setlocal enabledelayedexpansion
set p1=t1.txt
set p2=t2.txt
for /f "tokens=*" %%a in (%p1%) do (
    set nl=%%a
    if not "%n1%"=="%n1:texto=%" (
        set n1=replace with this text
    )
    echo !n1! >> %p2%
)
pause>nul
exit
  • 1
    This is easy in C#. Don't use this batch language, it's like nearly 40 years old. Use something more modern. Powershell could do it. – C.J. Jul 10 '18 at 17:51
  • 3
    This is the most asked reason for failing batches. If setting and using a variable in a (code block) enable delayed expansion **AND** replace the `%` enclosing a variable with an `!` (not the for meta variables) you do this once correctly in the echo, but miss it in the if command. `if not "!n1!"=="!n1:texto=!"` –  Jul 10 '18 at 18:18
  • Possible duplicate of [Variables in batch not behaving as expected](https://stackoverflow.com/questions/30282784/variables-in-batch-not-behaving-as-expected) – Squashman Jul 10 '18 at 18:42
  • 1
    Why are you using delayed expansion for echoing but not for comparing?? – aschipfl Jul 10 '18 at 20:56

2 Answers2

1

Your task is possible with pure batch scripting, but it is not that trivial when you want it in a safe manner, with respect to certain special characters and to not lose empty lines of the original file. So here is a way:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_IFILE=t1.txt"   & rem // (input file to be processed)
set "_OFILE=t2.txt"   & rem // (output file to be returned)
set "_WORD=\<texto\>" & rem // (string to search in lines, `findstr` regular expression)
set "_REPL=replace with this text" & rem // (text to replace lines containing the word)

rem // Output to console if no output file is provided:
if not defined _OFILE set "_OFILE=con"
rem /* Initialise a loop counter which should be synchronous to the line number, unless
rem    a line matches the search string, so it becomes skipped and the numbers differ: */
set /A "LCNT=0"
rem // Write to output file:
> "%_OFILE%" (
    rem /* Read from input file, prefix every line by a line number + `:`, skip lines
    rem    matching the search string: */
    for /F delims^=^ eol^= %%L in ('cmd /V /C findstr /V /N /R /C:"!_WORD!" "!_IFILE!"') do (
        rem // Split off the line number from the line string:
        for /F "delims=:" %%K in ("%%L") do (
            rem // Increment loop counter:
            set /A "LCNT+=1"
            rem // Store current line (including line number prefix):
            set "LINE=%%L"
            rem // Toggle delayed expansion in order to avoid trouble with `!`:
            setlocal EnableDelayedExpansion
            rem // Check whether loop counter and line number differ:
            if !LCNT! lss %%K (
                rem /* Numbers differ, hence return the replace line as many times
                rem    as the difference is: */
                set /A "DIFF=%%K-LCNT"
                for /L %%D in (1,1,!DIFF!) do echo(!_REPL!
            )
            rem // Return line with line number prefix removed:
            echo(!LINE:*:=!
            endlocal
            rem // Synchronise loop counter with line number:
            set "LCNT=%%K"
        )
    )
)

endlocal
exit /B

Restrictions:

  • lines must not exceed a length of about 8190 characters (limited by for /F and variable lengths);
  • the search expression must not be longer than 254 characters (limited by findstr);
  • the input text file must be ASCII/ANSI-encoded (cmd might handle other files wrongly, depending on the current code page);
  • the input text file should not contain characters with a code above 0x7F (cmd might change such characters to others, depending on the current code page);
  • the input text file must have DOS/Windows-style line endings (Unix-style line endings are understood, but the output text file is going to have DOS/Windows-style line endings then; MAC-style line endings are not supported at all);
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Thank you very much for your response, it is what I was looking for. I am starting in this language and it is difficult to find reliable sources, do you have documentation to share? – Juan Comande Jul 11 '18 at 14:18
  • You're welcome! I usually refer to http://ss64.com/nt/, which provides way more information than the help pages (`/?`)... – aschipfl Jul 11 '18 at 14:29
0

The command FOR always ignores empty lines and by default it ignores also lines starting with a semicolon. On usage of delayed expansion also lines containing an exclamation mark are modified on execution of command line set nl=%%a in the loop. So a solution using FOR could be the completely wrong attempt depending on content of file t1.txt.

Download JREPL.BAT written by Dave Benham which is a batch file / JScript hybrid to run a regular expression replace on a file using JScript and store it in same directory as the batch file below.

@echo off
if not exist "%~dp0jrepl.bat" goto :EOF
if not exist "t1.txt" goto :EOF

call "%~dp0jrepl.bat" "^.*textto.*$" "replace with this text" /F "t1.txt" /O "t2.txt"

rem Compare the two files binary with ignoring the differences. Delete file
rem t1.txt if a line was modified by JREPL.BAT. Otherwise move file t1.txt
rem over t2.txt to keep the last modification date of file t1.txt for t2.txt.
%SystemRoot%\System32\fc.exe /B "t1.txt" "t2.txt" >nul
if errorlevel 1 ( del "t1.txt" ) else move /Y "t1.txt" "t2.txt"

jrepl.bat searches with a regular expression for lines containing textto and replaces all such lines by replace with this text. All other lines are copied unmodified from input file t1.txt to output file t2.txt.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • del /?
  • echo /?
  • fc /?
  • goto /?
  • if /?
  • move /?
  • rem /?
  • jrepl.bat /?
Mofi
  • 46,139
  • 17
  • 80
  • 143
  • Thanks, but it is a script that I need for many computers with different versions of windows and, in general, with low resources, so I prefer to do it entirely in batch. – Juan Comande Jul 11 '18 at 14:25
  • @JuanComande No problem. `jrepl.bat` works also on every Windows machine including Windows XP because of using Windows scripting host which is a native installed component of Windows. However, if the solution posted by [aschipfl](https://stackoverflow.com/users/5047996/aschipfl) is better for you, it's okay. I posted this solution because of nothing was posted by you about the character encoding of text file `t1.txt` and its content. There is perhaps a much easier __FOR__ loop possible, but we can't suggest one without knowing file content. – Mofi Jul 11 '18 at 15:19