Almost there... Need to find and replace. Using for loop - batch-file

I am trying to find and replace. I am using a for loop. I am learning but I am almost there. I have tried to find the answer but the sources have been a bit too confusing for me.
The delim is a blank space and as you can tell I am skipping 4 lines and doing the 2nd token.
I need what is found there at that spot to be replaced by var5a. I have it backwards, as I need %%F to equal var5a, not the other way around (as I have it written now). But don't know how to write it. Please explain how one can do this. I've tried using <<= but with no luck.
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
set var5a=!var5a!%%F
)
I'm learning so please be kind.

#ECHO OFF
SETLOCAL
:: Replace token 2 (space-separated) in line 5 of a file with REPLACEMENT
:: Assumed the file exists, etc. and no line begins ":"
SET replacement=THIS IS THE REPLACEMENT TEXT
DEL newfile.txt 2>nul
FOR /f "tokens=1*delims=:" %%i IN ('findstr /n /r "$" ^<oldfile.txt') DO (
IF %%i==5 (
FOR /f "tokens=1,2* delims= " %%L IN ("%%j") DO >>newfile.txt ECHO %%L %replacement% %%N
) ELSE (>>newfile.txt ECHO.%%j)
)
TYPE oldfile.txt
ECHO ==== separator =======
FC oldfile.txt newfile.txt
Results:
Line one should not be changed
Line two should not be changed
Line three should not be changed
Line four should not be changed
changeme iwillbereplaced but only on this line
and notbereplaced on subsequent lines
including the previous line which was empty
==== separator =======
Comparing files oldfile.txt and NEWFILE.TXT
***** oldfile.txt
Line four should not be changed
changeme iwillbereplaced but only on this line
and notbereplaced on subsequent lines
***** NEWFILE.TXT
Line four should not be changed
changeme THIS IS THE REPLACEMENT TEXT but only on this line
and notbereplaced on subsequent lines
*****
There are difficulties - especially if the lines in your file start with a colon or contain quoted-strings or any of the usual batch gotchas like %
So - at long last I've worked out what you're doing. You omitted the critical information that the string in %%F is not only quoted, it also contains spaces.
Had you said that in the first place, you'd be hours further into your project, and I'd be richer by a handful of aspirin.
In order to load %%F with a quoted string, I've read the string from a file.
#ECHO OFF
SETLOCAL enabledelayedexpansion
:: Replace token 2 (space-separated) in line 5 of a file with REPLACEMENT
:: Assumed the file exists, etc. and no line begins ":"
SET replacement="THIS IS THE REPLACEMENT TEXT"
DEL newfile.txt 2>NUL
:: iwbr.txt just contains
:: "i will be replaced"
:: on a single line for loading into %%F as that is the target to be replaced
FOR /f "delims=" %%F IN (iwbr.txt) DO (
FOR /f "tokens=1*delims=:" %%i IN (
'findstr /n /r "$" ^<oldfile.txt'
) DO >>newfile.txt (
IF %%i==5 (
SET newline=%%j
CALL SET newline=%%newline:%%F=%replacement%%%
ECHO.!newline!
) ELSE (ECHO.%%j)
)
)
TYPE oldfile.txt
ECHO ==== separator =======
FC oldfile.txt newfile.txt
Results:
Line one should not be changed
Line two should not be changed
Line three "should" not be changed
Line four should not be changed
changeme "i will bereplaced" but only on this line
and notbereplaced on subsequent lines
including the "previous" line which was empty
including this "unbalanced-quote line...
==== separator =======
Comparing files oldfile.txt and NEWFILE.TXT
***** oldfile.txt
Line four should not be changed
changeme "i will bereplaced" but only on this line
and notbereplaced on subsequent lines
***** NEWFILE.TXT
Line four should not be changed
changeme "THIS IS THE REPLACEMENT TEXT" but only on this line
and notbereplaced on subsequent lines
*****

If you have Henri in %%F and you want to set the value of Henri to the current value of var5a then
set %%F=!var5a!
(!var5a! if you want the RUN-TIME value of var5a (provided you have ENABLEDELAYEDEXPANSION mode invoked) or %var5a% if you want the PARSE-TIME value)
Program to demonstrate that the answer does work:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
::: WARNING! This will overwrite SCRIPT.VBS
DEL script.vbs 2>nul
FOR /l %%i IN (1,1,4) DO >>script.vbs ECHO ignore this line %%i
>>script.vbs ECHO Oh Henri - it does work
>>script.vbs ECHO Now try again
ECHO ===== here is script.vbs
TYPE script.vbs
ECHO ===== script.vbs ends
SET var5a=this is the value
SET henri
SET var5a
ECHO ===== now run the FOR loop...
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
set %%F=!var5a!
)
SET henri
GOTO :eof
Results:
===== here is script.vbs
ignore this line 1
ignore this line 2
ignore this line 3
ignore this line 4
Oh Henri - it does work
Now try again
===== script.vbs ends
Environment variable henri not defined
var5a=this is the value
===== now run the FOR loop...
Henri=this is the value
Now - what do you mean by "it doesn't work"? It certainly does. If it's not doing what you want it to do, then demonstrate WHY you make the claim, otherwise we're left guessing.
For instance, the above will also set try to the value because Henri isn't the ONLY token that's number 2, space-delimited after 4 lines of the source file. That's easy to fix (but I'm not a mindreader - just a humble programmer)
(set notsetyet=Y)
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
IF DEFINED NOTSETYET set %%F=!var5a!&(set notsetyet=)
)
But maybe "it didn't work" because it Henri was set to the wrong value. Or perhaps it crashed. We can't tell from your response.

Related

Batch program - delete all dividing symbols in numbers

I have a homework task which needed to be done using just batch script. I need to rewrite all the numbers in .txt file if they have dividing symbols . or , but those strings may contain both words and numbers. Also the result should stay in the same file.
For example:
Lorem ipsum dolor 12.3254
2556,4646 ex commodo
would become
Lorem ipsum dolor 123254
25564646 ex commodo
I started with some code that looks like this:
#echo off
setlocal EnableDelayedExpansion
SET verfile=%1
FOR /f "tokens=* delims= " %%A IN (%verfile%) DO (
SET "Var=%%A"
FOR /l %%i IN (0, 1, 9) DO (
echo !Var! | findstr "."
IF %ERRORLEVEL% EQU 0 (
)
)
And now I have no idea how to continue it.
Can you help me please?
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory and filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q73553463.txt"
:: remove variables starting #
FOR /F "delims==" %%b In ('set # 2^>Nul') DO SET "%%b="
SET /a linecount=0
:: Read entire file into memory
FOR /f "usebackqdelims=" %%e IN ("%filename1%") DO (
rem next line number
SET /a linecount +=1
rem record in memory
SET "#!linecount!=%%e"
)
:: process each line removing [,.] following a digit
:: and report to original file
(
FOR /L %%e IN (1,1,%linecount%) DO (
FOR %%c IN ("." ",") DO FOR /L %%y IN (0,1,9) DO SET "#%%e=!#%%e:%%y%%~c=%%y!"
ECHO !#%%e!
)
)>"%filename1%"
TYPE "%filename1%"
GOTO :EOF
The set # command will generate a list like
#whatever=something
#whateverelse=somethingelse
for all variables that are currently set and start #.BUT it would be unusual to have any variable set that starts # so set would generate an error. The 2^>nul sends any error-report (on standard device stderr, which is device 2) to nul that is, nowhere. The caret ^ is required because cmd needs to distinguish between a part of the set command and a part of the for.
The for/f...%%b using delims of = generates "tokens" from the list generated by the set. Tokens are separated by any sequence of any of the delimiters specified between = and ", and by default, "token1" is selected, so the strings applied to %%b are
#whatever
#whateverelse
and these variables need to be set to nothing.
See for /? from the prompt for documentation on for or browse thousands of examples on SO.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces which can cause chaos. Once bitten, twice shy.
Then we read the file. Using "delims=" sets no delimiters, hence the whole line forms "token 1" which is assigned to %%e. The usebackq changes the meaning of " so that a double-quoted filename may be used. The filename I've used includes a Space but if there are no separator characters in the name, the quotes and usebackq can be omitted (again, ref for/?)
Then add 1 to the linecount and record the line in memory by assigning it to #!linecount!. The !linecount! is required because linecount is varying within the block (parenthesised sequence of lines) - and with delayedexpansion enabled, %linecount% yields the value when the block was encountered, and !linecount! the run-time or instantaneous value - as it changes within the block.
Stephan's DELAYEDEXPANSION link
So - having established #1, #2..#%linecount% with the lines from the file, we can process those variables and produce a replacement file.
Note that there is a block formed by ( for...%%e...)>filename. This allows any echoed data within the block to be redirected to the file. > redirects to a new version whereas >> would append to any existing file.
So - we iterate through all of the #linenumber lines using a for /L on %%e. For each of these, we set %%c to "." and %%y to 0 to 9 and then replace any string %%y%%c with %%y (3. gets replaced by 3 for example). Then repeat with %%c set to ",". set /? provides documentation and browsing SO examples.
But Woah, Neddie! There's a little trick here. , is a list-separator so (. ,) won't work - it will be treated as (.). Using the quotes allows cmd to read each character separately, and we need to use %%~c instead of %%c to dump the quotes.
So - take a look around. You can do a lot with batch if you're devious enough. And no doubt you'll be challenged if you present this solution. Be ready to explain it. A really good way to follow what's happening is to turn echo on and watch the magic step-by-step. Use pause liberally and perhaps remove the >"%filename1%" to prevent the report going to the file while you're observing what's happening.
#echo off
setlocal enabledelayedexpansion
set "verfile=%~1"
echo before:
type "%verfile%"
for /f "usebackq tokens=*" %%a in ("%verfile%") do (
set strline=%%a
set strline=!strline:.=!
set strline=!strline:,=!
echo !strline!>>"%verfile%.tmp"
)
echo.
echo after:
type "%verfile%.tmp"
del /f /q "%verfile%.tmp"

First character disappearing from command reading [Batch file]

I'm trying to read the output of a command (which outputs into multiple lines), and use an arbitrary number of those lines. Because I know neither the number of total lines, nor the number of lines that will be used, I need to analyse and possibly use each line in a loop, which is why I have setlocal enabledelayedexpansion.
Below is a snippet of the code that shows the process of taking the command and reading each line (not using it yet, just reading it to make sure this works (which it doesn't)):
#echo off
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in ('svn status') do (
echo %%i
set file=%%i
echo *!file!
)
The problem that I'm running into is that the %%i values that are being read in are not correct in the for line. The first character is missing from the first line of the input (which is important because I use the first line to decide whether or not to use that line).
The output I get from my code looks like this:
Dir0\TestDoc7.txt
? StatusFile.txt
Whereas if I run this code:
copy /y NUL StatusFile.txt >NUL
>StatusFile.txt (
svn status
)
(Which is just another way of me seeing what the real output of svn status is) I get a proper output into the text file:
! Dir0\TestDoc7.txt
? StatusFile.txt
I'm probably making a fairly clear mistake as I'm rather new to batch scripting.
Thanks in advance.
The cause is EnableDelayedExpansion which will eat the exclamation marks,
Your choice of tokens=* will also strip all leading spaces from the lines.
#echo off
for /f "tokens=1*" %%A in ('svn status') do (
if "%%A" equ "!" (
Rem do whatever
) else If "%%A" equ "?" (
Rem do something else rest of the line is in %%B
) else (
Rem no ! or ? first space sep. content is in %%A rest of the line is in %%B
)
)

Combine lines in text file using batch

I want to make a program that takes the content of the second line of a text file and puts it on the first. (It doesn't matter if the second doesn't get edited)
for /f "tokens=1" %%t in (file.txt) do set string1=%%t
for /f "tokens=2" %%t in (file.txt) do set string2=%%t
echo %string1%%string2%>file.txt
I have two issues hat I can't seem to be able to fix.
One: the loops only include the first word of each line in the variables.
Two: Echo doesn't replace the first line of the file with the variables given and instead writes ECHO command deactivated (I have the French version of Windows 10 and simply translated what got written in the file, the text in English Windows version might be slightly different, but you get the idea)
If you have any suggestions, I would appreciate if you explain what the code you provide does (I always like to learn)
Your question is not clear and can be understood in several different ways. Anyway, this management is simpler with no for command:
#echo off
setlocal EnableDelayedExpansion
< file.txt (
rem Takes the content of the first line
set /P "line1="
rem Takes the content of the second line and puts it on the first
set /P "line2="
echo !line1!!line2!
rem It doesn't matter if the second line doesn't get edited
echo !line2!
rem Copy the rest of lines
findstr "^"
) > output.txt
move /Y output.txt file.txt
The FOR command uses a space as a delimiter by default. So you have to tell it to not use any delimiters with the DELIMS option. Also, you should be able to do this with a single FOR /F command. Just hold the previous line in a variable.
#ECHO OFF
setlocal enabledelayedexpansion
set "line1="
(for /f "delims=" %%G in (file.txt) do (
IF NOT DEFINED line1 (
set "line1=%%G"
) else (
echo !line1!%%G
set "line1="
)
)
REM If there are an odd amount of lines, line1 will still be defined.
IF DEFINED line1 echo !line1!
)>File2.txt
EDIT: I think I completely misunderstood your question. Once you clarify your question I will repost a code solution if needed.
Use skip to omit the first line and write the 2nd line twice. In general an edit of a file implies a rewrite to a new file and possibly a rename to retain the old file name.
:: Q:\Test\2018\07\25\SO_51508268.cmd
#Echo off
Set "flag="
( for /f "usebackq skip=1 delims=" %%A in ("file1.txt") Do (
If not defined flag (
Echo=%%A
Set flag=true
)
Echo=%%A
)
) >file2.txt
Del file1.txt
Ren file2.txt file1.txt
After running the batch a file1.txt with initially numbered lines 1..5 looks like this:
> type file1.txt
2
2
3
4
5

batch to scan a file for a string and get the line above in all occurrences, then>output.txt

I have a list.txt file like this little sample:
banana.zip
xxx / yyy / zzz
orange.zip
kkklllmmm
lemon.zip
abc / def / ghi
apple.zip
xxx / yyy / zzz
pineaple.zip
kkklllmmm
I need to filter the .zip lines based on the line below.
So, if I choose the string kkklllmmm, the result needs to be:
orange.zip
pineaple.zip
I have started a batch script, but it's incomplete:
#echo off
set "input_list=list.txt"
set "string=kkklllmmm"
set "output_list=output.txt"
for /f "delims=:" %%# in ('findstr /n /c:"%string%" "%input_list%"') do (
set "line=%%#"
set /a "lineBefore=line-1"
for /f "delims=:" %%a in ("[CONTENT OF %lineBefore%]") do echo %%a>%output_list%)
I would appreciate a help to fix this code and make it work.
Thanks.
to explicate Squashman's suggestion:
#echo off
setlocal enabledelayedexpansion
set "input_list=list.txt"
set "string=kkklllmmm"
set "output_list=output.txt"
(for /f "delims=" %%# in (%input_list%) do (
REM if current line is your searchstring, then echo the previous line:
if "%%#"=="%string%" echo !line!
REM set the variable to the current line:
set "line=%%#"
)) > "%output_list%"
Note: I used empty delimiter to read the whole line. If you intentionally used : for some reason (aside "some character that isn't in the file to read the whole line"), just reimplement it.
Edit:
How did you get the previous line? by setting the line variable after comparing %%#(the current line), so with each turn of the loop at the end line is the current line, but on the next turn at the beginning, it will be the "previous line" (and become the "current line" only at the end of the turn)
I need to find more than one string: a bit more complicated: add a second for to process each search string. Note the syntax of the set "string=..." line. To also process strings with spaces, this syntax is mandatory. (if there were no spaces, it would simplify to set "string=string1 string2 string3"), but to process the spaces, each substring has to be quoted too:
set "string="string 1" "string 2" "string 3"")
#echo off
setlocal enabledelayedexpansion
set "input_list=list.txt"
set "string="kkklllmmm" "xxx / yyy / zzz""
set "output_list=output.txt"
(for /f "delims=:" %%# in (%input_list%) do (
for %%a in (%string%) do (
echo %%#|findstr /xc:%%a >nul && echo !line!
)
set "line=%%#"
)) > "%output_list%"
The outer for (%%#) gets one line at a time.
The inner for processes each substring in the list at a time.
The next line echoes the line and tries to find the substring (switches
x=compare the complete line,
c=search for the literal string (including the spaces, else it would try to find each word in the string, e.g. xxx, /, yyy etc.)
&& executes the command echo !line! only, if the previous command succeeded (findstr found a match)
You use a PowerShell command:
powershell -nop -c "sls List.txt -Patt 'kkklllmmm' -Co 1,0|%{$_.Context.PreContext}">output.txt
Or wrap it into a batch:
#echo off
setlocal enabledelayedexpansion
set "input_list=list.txt"
set "string=kkklllmmm"
set "output_list=output.txt"
(powershell -nop -c "sls '%input_List%' -Patt '%string%' -Co 1,0|%%{$_.Context.PreContext}"
)>"%output_list%"
Sample output
orange.zip
pineaple.zip

print specific lines from a batch file

I am trying to print Line 4, Col 21-50 out of a text file, can this be simply done under Windows somehow? I've been trying to do this:
FOR /F "usebackq tokens=1 delims=-" %G IN (%COMPUTERNAME%.txt) DO ECHO %G
This is just working out terribly. Can't I just print a specific set of lines?
I need this script to be run on multiple computers, ideally I'd like to convert it to a variable for use with slmgr -ipk, maybe someone has a better suggestion?
Contents of text file (I want the XXXXX-XXXXX-XXXXX-XXXXX-XXXXX portion):
==================================================
Product Name : Windows 7 Professional
Product ID : 00371-OEM-9044632-95844
Product Key : XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
Installation Folder : C:\Windows
Service Pack : Service Pack 1
Computer Name : LIBRA
Modified Time : 6/4/2015 7:26:54 PM
==================================================
if you want only the "Product Key" line you can try with
type %COMPUTERNAME%.txt|find /i "Product Key"
or
for /f "tokens=2 delims=:" %%# in (' type %COMPUTERNAME%.txt^|find /i "Product Key"') do echo %%#
For the task at hand, npocmaka's answer is the best suitable approach, as it does not insist on a fixed position of the string to extract from the file.
However, I want to provide a variant that sticks to a certain position.
The following code extracts the string placed at columns 21 to 50 in line 4 of file list.txt (the result is echoed (enclosed in "") and stored in variable LINE_TXT (without ""):
#echo off
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" "list.txt"'
) do (
if %%L equ 4 (
set "LINE_TXT=%%M"
goto :NEXT
)
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
The goto :NEXT command terminates the for /F loop at the given line; this is not mandatory but will improve performance for huge files (as long as the given line number is quite small).
To be more flexible, the following code can be used (define the string position in the initial set block):
#echo off
rem Define the string position here:
set FILE_TXT="list.txt"
set LINE_NUM=4
set COL_FROM=21
set COL_UPTO=50
setlocal EnableDelayedExpansion
set /A COL_UPTO-=COL_FROM
set /A COL_FROM-=1
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" %FILE_TXT%'
) do (
if %%L equ %LINE_NUM% (
set "LINE_TXT=%%M"
if defined LINE_TXT (
set "LINE_TXT=!LINE_TXT:~%COL_FROM%,%COL_UPTO%!"
)
goto :NEXT
)
)
:NEXT
endlocal & set "LINE_TXT=%LINE_TXT%"
echo."%LINE_TXT%"
Both of the above code snippets rely on the output of findstr /N /R ".*", which returns every line that matches the regular expression .*, meaning zero or more characters, which in turn is actually true for every line in the file; however, the switch /N defines to prefix each line with its line number, which I extract and compare with the originally defined one.
Here is another variant which uses for /F to directly loop through the content (lines) of the given text file, without using findstr:
#echo off
for /F "usebackq skip=3 eol== delims=" %%L in (
"list.txt"
) do (
set "LINE_TXT=%%L"
goto :NEXT
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
This method has got the better performance, because there is the skip option which skips parsing of and iterating through all lines (1 to 3) before the line of interest (4), opposed to the findstring variant.
However, there is one disadvantage:
for /F features an eol option which defines a character interpreted as line comment (and defaults to ;); there is no way to switch this option off as long as delims= defines no delimiters (last position in option string), which is mandatory here to return the line as is; so you have to find a character that does not appear as the first one in any line (I defined = here because your sample text file uses this as header/footer character only).
To extract a string from line 1, remove the skip option as skip=0 results in a syntax error.
Note that goto :NEXT is required here; otherwise, the last (non-empty) line of the file is extracted.
Although for /F does not iterate any empty lines in the file, this is no problem here as the skip option does not check the line content and skip over empty lines as well.
Finally, here is one more approach using more +3 where no text parsing is done. However, a temporary file is needed here to pass the text of the desired line to the variable LINE_TXT:
#echo off
set LINE_TXT=
more +3 "list.txt" > "list.tmp"
set /P LINE_TXT= < "list.tmp"
del /Q "list.tmp"
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
exit /B 0
This method avoids for /F and therefore the problem with the unwanted eol option as mentioned in the above solution. But this does not handle tabs correctly as more substitutes them with spaces (8 indent spaces as per default and configurable by the /Tn switch where n is the number of spaces).

Resources