Page 1 of 1

Replace specific line in a .txt with text

Posted: 21 May 2016 04:16
by (_osd_)
Hello,
I need a script that replaces a specific line in a .txt file with something, for example:

file.txt:

Code: Select all

batch
is
dumb


and replace the last line with "awesome", so file.txt will look like this:

Code: Select all

batch
is
awesome

after the script ran.

How can you do that?

Re: Replace specific line in a .txt with text

Posted: 21 May 2016 15:02
by aGerman
I fear you have to rewrite the file.

Code: Select all

@echo off &setlocal DisableDelayedExpansion
set "file=test.txt"
set "lastline=awesome"

setlocal EnableDelayedExpansion
<"!file!" >"!file!.tmp~" (
  for /f %%i in ('type "!file!"^|find /c /v ""') do for /l %%j in (2 1 %%i) do (
    set "line=" &set /p "line="
    echo(!line!
  )
)
>>"!file!.tmp~" echo(!lastline!
move /y "!file!.tmp~" "!file!"

Regards
aGerman

Re: Replace specific line in a .txt with text

Posted: 22 May 2016 09:32
by thefeduke
Here is another simple way.

Code: Select all

ECHO Off
setlocal EnableDelayedExpansion

Set "file=%temp%\testing_Input.txt"
(for /f %%i in ('Type %file%') do (
    set "line=%%i"
    If /I "!line!" EQU "dumb" set "line=awesome"
    echo(!line!
))>%temp%\testing_Output.txt

Echo.Contents of: "%temp%\testing_Output.txt"
Type %temp%\testing_Output.txt
Copy /Y %temp%\testing_Output.txt %file%

Exit /B
John A.

Re: Replace specific line in a .txt with text

Posted: 22 May 2016 10:28
by aGerman
John

Delayed expansion permanently switched on will cause errors with lines that contain exclamation marks. FOR /F needs at least "delims=" to read the whole line. Nevertheless it will always decline blank lines.

Code: Select all

The teacher angry: Shut up! Be quiet and listen to me!

batch
is
dumb

"! Be quiet and listen to me!" would be parsed as (an undefined) variable.

Regards
aGerman

Re: Replace specific line in a .txt with text

Posted: 22 May 2016 21:53
by thefeduke
aGerman wrote:Delayed expansion permanently switched on will cause errors with lines that contain exclamation marks. FOR /F needs at least "delims=" to read the whole line. Nevertheless it will always decline blank lines.
I always enjoy your mentoring.
thefeduke wrote:Here is another simple way.
Both of our examples were very specific to the data supplied. One changed the last record, regardless of content and mine looked for "dumb" alone in a series of one word records. I did keep it very simple, but your observation brings to mind a similar problem.

Perhaps you could comment on the statement:
thefeduke wrote:Although it functions within the limited scope that you described, it does break down if the character '!' is imbedded one or more times within a larger string.
that I made in another topic: "Post subject: Re: FindRepl.bat:New regex utility to search and replace strings" at http://www.dostips.com/forum/viewtopic.php?p=46588#p46588 . It sounds like I should be getting creative at turning delayed expansion off at certain points in the processing to handle those situations. I did not have the skills then, but there may be hope.

John A.

Re: Replace specific line in a .txt with text

Posted: 23 May 2016 10:52
by aGerman
Of course my reply was no offence :wink:
I fully agree with you that your script is sufficient to successfully change the example file. Out of my experiences the real files are almost never that simple though. I should mention that my script is also not bulletproof because it will fail as soon as the line length exceeds 1021 characters. That's a condition where FOR /F should be prefered. FINDSTR /N would help to preserve blank lines.

Code: Select all

@echo off &setlocal DisableDelayedExpansion
set "file=test.txt"
set "lastline=awesome"

for /f %%i in ('type "%file%"^|find /c /v ""') do set "last=%%i"
<"%file%" >"%file%.tmp~" (
  for /f "delims=" %%i in ('type "%file%"^|findstr /n "^"') do (
    set "line=%%i"
    setlocal EnableDelayedExpansion
    for /f "delims=:" %%j in ("!line!") do if %%j equ %last% (
      echo(!lastline!
    ) else (
      echo(!line:*:=!
    )
    endlocal
  )
)
move /y "%file%.tmp~" "%file%"

As you can see assigning a variable is safe with delayed expansion switched off while you can safely work with the variable content if delayed expansion is enabled.

Regards
aGerman

Re: Replace specific line in a .txt with text

Posted: 23 May 2016 14:32
by Thor
This batch file make use of a temp file.

Code: Select all

::"text_replace.bat"
@echo off
setlocal EnableDelayedExpansion
cls
echo input.txt:
(
echo Batch
echo is
echo dump
) > input.txt
type input.txt
echo.
set count=0
(
   for /f "tokens=*" %%a in ('type input.txt') do (
      set /a count+=1
      if !count! equ %1 (
         echo %~2
      ) else (
         echo %%a
      )
   )
)>temp.txt
copy /y temp.txt input.txt >nul
type input.txt
del temp.txt


To test it just run this batch like this:
% text_replace 3 awsome

Re: Replace specific line in a .txt with text

Posted: 23 May 2016 18:17
by Thor
Another way without the need to provide the line index is like this:

"text_replace.bat"

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cls

echo input.txt:
(
echo Batch
echo is
echo dumb
) > input.txt
type input.txt
echo.

for /f "delims=:" %%a in ('type input.txt ^|findstr /nc:^"%~1^"') do set index=%%a
set count=0
(
for /f "tokens=*" %%a in ('type input.txt') do (
   set /a count+=1
   if !count! equ !index! (
      echo %~2
   ) else (
      echo %%a
   )
)
)>temp.txt
copy /y temp.txt input.txt >nul
type input.txt
del temp.txt


To test it just do like this:

Code: Select all

text_replace dumb awsome

Re: Replace specific line in a .txt with text

Posted: 25 May 2016 13:51
by thefeduke
aGerman wrote:As you can see assigning a variable is safe with delayed expansion switched off while you can safely work with the variable content if delayed expansion is enabled.
Thank you for that inspiring example. Manipulating the elusive exclamation (!) mark using just batch has baffled me for some time now. I followed your model and , when it worked, I incorporated some more general poison character handling. So here is a test file:

Code: Select all

batch
is
dumb!
=%"^
Very dumb, indeed!
!
The teacher angry: Shut up! Be quiet and listen to me!
<>&|
The=teacher%angry:"Shut up!^Be<quiet>and&listen|to me!
Here is the display when naming the file as the first argument to the batch script:

Code: Select all

C:\Users\Zani\Scripts>poisoner poisoned.txt
batch
is
awesome.

Very awesome, indeed.
.
The teacher angry: Shut up. Be quiet and listen to me.

The teacher angry: Shut up. Be quiet and listen to me.

The code was set to replace '!' with '.' and the other poison characters to <blank>. I did not add the file creation and rename in this example.

Code: Select all

   @Echo Off
::  The first argument is the input filename
    SETLOCAL Enabledelayedexpansion
    set "DelChars== %% ^" ^^^^ ^^! ^< ^> ^& ^| "
    set "DelChars=!DelChars: =!"
    set  XclChar=^^!
    set "XclNewChar=."
    set "AnyNewChar= "
    SETLOCAL Disabledelayedexpansion
    For /F "usebackq tokens=* delims=" %%I in ("%~1") DO (
        set "str=%%I[[EoS]]"
        SETLOCAL Enabledelayedexpansion
        For /L %%A in (32767,-128,0) do (
            set Tstr=!str:~%%A!
            If /I "!Tstr!" EQU "" Set LastEmpty=%%A
        )
        For /L %%A in (!LastEmpty!,-1,0) do (
            set Tstr=!str:~%%A,7!
            If /I "!Tstr!" EQU "[[EoS]]" Set EndTag=%%A
        )
        Set /A EndStr=EndTag-1
        For /L %%A in (!EndStr!,-1,0) do (
            set Tstr=!str:~%%A,1!
            If /I "!Tstr!" EQU "!XclChar!" (
                set NEWstr=%xclNewChar%!NEWstr!
            ) Else (
                set NEWstr=!Tstr!!NEWstr!
            )
        )
Rem.        Echo.!NEWstr!
            Set "PoisonString=!NEWstr!"
            For /L %%C in (0,1,8) do (
                Set "Base=!DelChars:~%%C,1!"
                For /L %%P in (!EndStr!,-1,0) do (
                    Set "Test=!PoisonString:~%%P,1!"
                    If /I "!Test!" EQU "!Base!" (
                        Set "Sterile=%AnyNewChar%!Sterile!"
                    ) Else (
                        Set "Sterile=!PoisonString:~%%P,1!!Sterile!"
                    )
                )
                Set "PoisonString=!Sterile!"
                Set "Sterile="
            )
            set PoisonString=!PoisonString:dumb=awesome!
        Echo.!PoisonString!
        EndLOCAL
    )

Exit /B

Of course, it is more expeditious to use hybrid scripts but I just had to try this as a batch script. Thank you again, aGerman, for directing me to a method.

John A.