Batch how to change a single line? - windows

I have some code, that I am trying to use to take this file, and wait until it gets to line 39. And then at line 39 I want it to print something else. But i can't get it to ever get through that IF condition.
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line+=1
if !line!==39 (
echo REPLACED TEXT>>%tempfile%
) else (
echo %%a>>%tempfile%
) )
After the
set /a line+=1
I tried
ECHO line
to see what my line variable was after I set it to line+=1. I received back all 0s. Does anyone see why my code is not incrementing the line like its supposed to? But when it outputs the file, it outputs perfectly except that line 39 that does not change. It remains the same.

I think you're missing setlocal ENABLEDELAYEDEXPANSION and you need to use echo !line! if you want to output inside the block:
#echo off
setlocal ENABLEDELAYEDEXPANSION
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line = !line!+1
ECHO !line!
if !line!==39 (
echo REPLACED TEXT>>%tempfile%
) else (
echo %%a>>%tempfile%
) )

Related

Batch - Get block of text between flags, output and iterate over all files

I've got a bunch of text files in a directory that have a block of text I want to extract between two strings into a new text file of a similar name. I've got the single file working but think I've come unstuck with looping through all .txt files. Maybe at the "goto" command?
Here is the original, single file code I used:
Batch File - Find two lines then copy everything between those lines
~Top Break
foobar
~ more data title
more foobar
~Bottom Break
Garbage data
I have this code that works for a single file called FileNumber1.txt.
#echo off
set "FIRSTLINE=~Top Break"
set "LASTLINE=~Bottom Break"
set "INFILE=FileNumber1.txt"
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "%INFILE%_MyData.txt" (
rem findstr configured so that each line in a file is given a "1:" number and colon.
for /F "delims=" %%L in ('findstr /N "^" "%INFILE%"') do (
set "LINE=%%L"
setlocal EnableDelayedExpansion
rem this LINE=!LINE:*:=! removes the any character before the Colon. *:
set "LINE=!LINE:*:=!"
rem this block of code checks to see if line of text = Firstline variable, if so FLAG = TRUE
if "!LINE!"=="%FIRSTLINE%" (
endlocal
set "FLAG=TRUE"
rem this block of code checks to see if line of text = Lastline variable, if so goto :Continue and end the loop
) else if "!LINE!"=="%LASTLINE%" (
endlocal
goto :CONTINUE
) else if defined FLAG (
echo(#!LINE!
endlocal
) else (
endlocal
)
)
)
:CONTINUE
endlocal
NewFile1_MyData.txt Output:
foobar
~ more data title
more foobar
I've tried to wrap this in another "FOR" loop that looks for all txt files in the same directory.
This is my code that isn't working.
#echo off
set "FIRSTLINE=~Top Break"
set "LASTLINE=~Bottom Break"
for /F %%f in (*.txt) do (
set "INFILE=%%f"
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "%INFILE%_OldHeader.txt" (
rem findstr configured so that each line in a file is given a "1:" number and colon.
for /F "delims=" %%L in ('findstr /N "^" "%INFILE%"') do (
set "LINE=%%L"
setlocal EnableDelayedExpansion
rem this LINE=!LINE:*:=! removes the any character before the Colon. *:
set "LINE=!LINE:*:=!"
rem this block of code checks to see if line of text = Firstline variable, if so FLAG = TRUE
if "!LINE!"=="%FIRSTLINE%" (
endlocal
set "FLAG=TRUE"
rem this block of code checks to see if line of text = Lastline variable, if so goto :Continue and end the loop
) else if "!LINE!"=="%LASTLINE%" (
endlocal
goto :CONTINUE
) else if defined FLAG (
echo(#!LINE!
endlocal
) else (
endlocal
)
)
)
endlocal
:CONTINUE
))
The Command window gets to the "for /F" statement and exits.
Mmm... I would change the method to extract the lines for a simpler one based on lines to skip at beginning of file and number of lines to extract. After that, I would use a for to process all files and call a subroutine to extract the lines:
#echo off
setlocal EnableDelayedExpansion
set "FirstLine=~Top Break"
set "LastLine=~Bottom Break"
rem Process all text files in this folder
for %%f in (*.txt) do (
rem Search for First line and Number of lines
set "FirstNum="
for /F "delims=:" %%n in ('findstr /C:"%FirstLine%" /C:"%LastLine%" /N "%%f"') do (
if not defined FirstNum (
set "FirstNum=%%n"
) else (
set /A "LastNum=%%n-FirstNum-1"
)
)
rem Copy the lines
call :CopyLines >"%%~Nf_MyData.out" "%%f", !FirstNum!, !LastNum!
)
ren *.out *.txt
goto :EOF
:CopyLines File, Skip, Num
set "Num=%3"
for /F "usebackq skip=%2 delims=" %%a in (%1) do (
setlocal DisableDelayedExpansion
echo %%a
endlocal
set /A Num-=1
if !Num! equ 0 exit /B
)
exit /B

How to use % or ! in batch script

I write script like this:
#ECHO OFF
setlocal EnableDelayedExpansion
set "remove=ABC"
echo. %remove%
Set FILENAME="456_789_ABC00011092_789_EFGHIK_56893.mpg"
for %%a in (%FILENAME:_=" "%) do (
set TEN=%%a
echo. %AB%
set "remove_1=ABC"
echo. %remove_1%
Set _TEN=!TEN:%remove%=!
echo. %_TEN%
Set i=0
IF !_TEN! NEQ !TEN! (
set /A i+=1
set "String[!i!]=%%~a"
)
)
pause
exit
Why echo. %AB% echo. %remove_1% result is
I replace % by !. It's work fine but command Set _TEN=!TEN:!remove_1!=! not run
Edit - (from the additional question currently posted as an answer)
When I use FindStr command like this:
for %%a in (%FILENAME:_=" "%) do (
echo %%a | findstr /I /R /C:"ABC" >nul
ECHO %errorlevel%
if "%errorlevel%" equ "0" (
set /A i+=1
set "String[!i!]=%%~a"
)
)
Why errorlevel always = 0
%AB% has not been defined within your posted script, so as it has no value will not be echoed, you will just get an empty line due to the . after echo. Because remove_1 is being set within the loop, (code block), you should be using the delayed expansion syntax, Echo !remove_1!. It is the same for echo. %_TEN%, i.e. Echo !_TEN!, and would have been Echo !AB! had it previously been defined. In order to get the double expansion needed to Set your _TEN variable, you could use a pseudo Call:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Set "TEN=%%A"
Echo. !AB!
Set "remove_1=ABC"
Echo !remove_1!
Call Set "_TEN=!TEN:%%remove_1%%=!"
Echo !_TEN!
Set "i=0"
If "!_TEN!" NEq "!TEN!" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Pause
Exit /B
In your second related question, initially posted as an answer and now added as an edit to your original question; because the error level is being set within the loop, (code block), you should be using the delayed expansion syntax, !errorlevel!
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul
Echo !errorlevel!
If "!errorlevel!"=="0" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B
Or if you don't need to Echo each error level to the screen, you can use a conditional statement &&:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul && (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B

Print and assign array values to another variable in batch file

I face some problem while trying to print array values as shown below:
#echo off
setlocal EnableDelayedExpansion
set args=
set /A argc=0
SET /A argn=0
for %%a in (%*) do (
SET args[!argn!]=%%a
SET /A argn+=1
)
FOR %%q in (%*) DO (
echo !args[%argc%]! //not able to print the value
call echo %%args[!argc!]%% // this worked
if "%%q" == "--snap" (
set /A argc+=1
set SNAP=!args[%argc%]! //this didn't work
)
if "%%q" == "--source" (
set /A argc+=1
call SET "SOURCE=%%args[!argc!]%%" //this didn't work too
)
set /A argc+=1
)
Using this segment of code prints only the first value of the array but the other method of using for /l works fine.
How do i correct this?
Is it possible to store this array value in any other variable? If so, how?
You are trying to echo a variable you haven't set, it will therefore be blank.
#Echo Off
SetLocal EnableDelayedExpansion
Set argc=0
For %%q In (%*) Do (
Set "args[!argc!]=%%q"
Rem The next line is for information only
Call Echo=%%args[!argc!]%%
Set/A argc+=1
)
Rem take a look at the variables
Set args[
Timeout -1
Assuming delayedexpansion has been invoked and argn has been set to #args+1 as reported in comments,
set /a argn-=1
for /L %%q in (0,1,%argn%) do echo !args[%%q]!
set /a argn+=1
would be my preference.

How to output exclamation mark with EnabledDelayedExapnsion?

I am working on editing an XML file, and approximately the first 10 lines are comments. And for xml comments are in the form
<!-- COMMENT HERE -->
But when using my code it does not output that ! mark, which screws up the comments in the xml. I understand that the ENABLEDELAYEDEXPANSION is doing this because it thinks the exclamation mark is expanding a variable. How could I get this to work?
Here is my code below
setlocal ENABLEDELAYEDEXPANSION
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line = !line!+1
if !line!==39 (echo REPLACED TEXT39>>%tempfile%
)else if(!line!==45 (echo REPLACED TEXT45>>%tempfile%
)else (echo %%a>>%tempfile%
))
EDIT1 Basically what it is supposed to do is output each line as it is, unless it is line 39 or 45. It works, except the ! marks in the comments don't get outputted and they are not comments anymore.
EDIT2
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
setlocal ENABLEDELAYEDEXPANSION
set /a !line! +=1
echo !line!
if !line!==39 (
echo REPLACED TEXT39>>%tempfile%
endlocal
)else if !line!==45 (
echo REPLACED TEXT45>>%tempfile%
endlocal
)else (
endlocal
setlocal DISABLEDELAYEDEXPANSION
echo %%a>>%tempfile%
endlocal
))
Here is the latest code I have been using. It works the best, but now the problem is that the variable "line" is not getting updated. I have a feeling that it is because of the "endlocal". The only problem is that I need the "endlocal" there otherwise I get an error
Maximum setlocal recursion level reached.
The problem is, I need to alternate between enableddelayedexpansion and disabledelayedexpansion so that my exclamation marks show up properly. But to do that I need to keep up with the "endlocal" calls which i think is messing up my line variable. Any thoughts?
You can't output the exclamation mark this way.
The exclamation mark is part of the content of %%a but while delayed expansion is enabled you can't access it, as it will be parsed after the %%a is epanded.
So you need to disable delayed expansion at all or temporary.
A sample for temporary disabling it
setlocal ENABLEDELAYEDEXPANSION
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line = !line!+1
if !line!==39 (
echo REPLACED TEXT39>>%tempfile%
) else if !line!==45 (
echo REPLACED TEXT45>>%tempfile%
) else (
setlocal DisableDelayedExpansion
echo %%a>>%tempfile%
endlocal
)
)
Or you don't use it at all, then you only need to get the if line=42 part working.
This uses the fact that modulo by 0, will produce an error (which is suppressed by 2>nul) and the variable stays unchanged, in this case they stay undefined.
setlocal DisableDelayedExpansion
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line+=1
set "notLine39="
set /a "notLine39=1%%(line-39)" 2>nul
set "notLine45="
set /a "notLine45=1%%(line-45)" 2>nul
if not defined line39 (
echo REPLACED TEXT39>>%tempfile%
) else if not defined line45 (
echo REPLACED TEXT45>>%tempfile%
) else (
setlocal DisableDelayedExpansion
echo %%a>>%tempfile%
endlocal
)
)
Edit: Added explanation to your changed question
This uses the toggling delayed expansion technic, described in SO: Batch files: How to read a file?
The trick is to be in disabledDelayedExpansion when transfering %%a to text, then switching to enabledDE and be able to use the extended syntax.
But don't forget the endlocal before the next loop starts.
setlocal DisableDelayedExpansion
set line=0
FOR /f "usebackqdelims=" %%a in ("%filename2%") do (
set /a line+=1
set "text=%%a"
setlocal EnableDelayedExpansion
if !line!==39 (
echo REPLACED TEXT39>>%tempfile%
) else if !line!==45 (
echo REPLACED TEXT45>>%tempfile%
) else (
echo %%a>>%tempfile%
)
endlocal
)

CMD script to find and replace a part of found line in file

I need to find all lines in myfile.txt containing word 'MyWord', and then replace a part of this string next way:
Original line:
...,31-01-2012,00,some_words_and_symbols_and_digits,MyWord,...
After replace:
...,31-01-2012,01,some_words_and_symbols_and_digits,MyWord,...
Please, help me to write this cmd script!
OK.. I have next code:
#echo off
set code=MyWord
set req=new request
FOR /F "usebackq delims=, tokens=1,2,3,4,5,6,7,8,9*" %%a in (MyFile.txt) do (
IF %%h==%code% (
SET tempstr=%%a,%%b,%%c,%%d,60,%%f,%%g,%%h,%%i
) ELSE (
SET tempstr=%%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,%%i
)
IF %%a==%req% (
SET echo %%a >> new.strings
) ELSE (
echo %tempstr% >> new.strings
)
)
#echo on
And I have in my file something like:
new request
...,31-01-2012,01,some_words_and_symbols_and_digits,MyWord,...
new request
...,30-11-2011,01,some_words_and_symbols_and_digits,OtherWords,...
But then I have error:
ELSE was unexpected at this time.
And If I'm trying simple next in the end
IF %%a==%req% SET tempstr=%%a
echo %tempstr% >> new.strings
Then I have only one last row instead of other else
You can use find command to filter the lines containing given text. As I see, the file is CSV. So you can use for /f to parse the lines found. Then you can echo all parsed files replacing the field you want.
This will replace all values in the 3rd column with "01"
#echo off
for /f "usebackq delims=, tokens=1,2,3,4,<put as many as you need>" %%A in (`find "MyWord" myfile.txt`) do echo %%A,%%B,01,%%D,<as many %%letters as tokens>
If you want to replace the value only on some lines, you can use if command inside for /f loop.
==== EDIT
The problem is with the value of req variable. It contains a space, so after substitution your second if statement has the following form:
IF %%a==new request (
so if %%a is equal to new it will execute request ( echo ...... ) and then ELSE is unexpected indeed. Enclose both %%a and %req% in quotation marks and the problem will disappear.
But I see also other problems. First, you have redundant set in your second if statement.
Second, you need to use delayed expansion of variables, or your echo %tempstr% won't work.
Your code after needed changes:
#Echo off
setlocal enabledelayedexpansion
set code=MyWord
set req=new request
FOR /F "usebackq delims=, tokens=1,2,3,4,5,6,7,8,9*" %%a in (MyFile.txt) do (
IF %%h==%code% (
SET tempstr=%%a,%%b,%%c,%%d,60,%%f,%%g,%%h,%%i
) ELSE (
SET tempstr=%%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,%%i
)
IF "%%a"=="%req%" (
echo %%a >> new.strings
) ELSE (
echo !tempstr! >> new.strings
)
)
endlocal

Resources