Page 1 of 2
SET a:b=c won't work
Posted: 09 Jul 2010 23:15
by espaiz
It's a search and replace script.
I have a file called "words.txt" that looks like this:
A template.txt file that looks like:
The result should be :
Code: Select all
example one
example two
example three
The idea is: the code below walks through every line inside word.txt, it uses word.txt's first line as a replace string ([###] ) and then uses other lines (one, two, three) as replacements in template.txt
Code: Select all
@echo off
setLocal EnableDelayedExpansion
set /a counter=0
::go through words line by line
for /f "delims=" %%w in (words.txt) do (
set replace=%%w
if !counter! EQU 0 (
set search=%%w
)
if !counter! NEQ 0 (
::read template and replace !search! with !replace!
for /f "delims=" %%l in (template.txt) do (
set old=%%l
set new=!old:search=replace!
echo !new!
)
)
set /a counter+=1
)
echo !counter!
The current result that I see is:
Code: Select all
example [###]
example [###]
example [###]
... search and replace didn't work.
This is my first experience with DOS, and I would appreciate any help. (spent hours researching this, it should work but it doesn't, what am I missing?)
Thanks
Re: SET a:b=c won't work
Posted: 10 Jul 2010 01:41
by alan_b
I do not see where you have defined the variable "counter"
I suspect you have not defined it before running the code you have shown,
and what you wrote as
"if !counter! EQU 0"
is understood by DOS as
"if EQU 0"
You have the same problem with
"if !counter! NEQ 0"
Alan
Re: SET a:b=c won't work
Posted: 10 Jul 2010 03:27
by espaiz
Sorry about that, I cut the counter part for some reason, updated first post.
Everything is working except "set new=!old:search=replace!"
Re: SET a:b=c won't work
Posted: 10 Jul 2010 05:15
by alan_b
You should NOT use labels inside of brackets.
You cause all sorts of mayhem to conditional clauses when they contain your
" ::read template and replace !search! with !replace!"
You do not seem to have set a variable with the name
old:search=replace
therefore there is no
old:search=replace
and you simply remove any variable called new with
set new=!old:search=replace!
I suggest you put various echo statements in your code so you can see where you go wrong. e.g.
ECHO !COUNTER!-!REPLACE!-!SEARCH!- etc etc.
do that for all the variables you have set
and for all the variables you thought were defined.
Alan
Re: SET a:b=c won't work
Posted: 10 Jul 2010 06:38
by espaiz
Thanks for you reply Alan.
About the :: ... I use this to comment the code, and as I understand that's the right way, please let me know how to write comments in a different way. I'm a programmer, but using DOS for the first time, and I got used to comment my code for those who will view it later...
(I did remove all of them now to make sure there are no conflicts of any kind)
Echoing !counter!-!replace!-!search! works everywhere, and echoing !old!/!new! in the second loop also works. So no missing variables anywhere. The structure "set new=!old:search=replace!" is what I found online. Writing it any differently would not work as well, I really tried many possible ways.
As I mentioned in the first post, I get the results, but just no replacement. Doing "set new=!old:search=replace!" would just print !old! with no replacement. There's something not right with my SET code, maybe %% o r % instead of ! should be used, or somehow combined.
p.s. is there a place I can read about %% % and ! difference?
Re: SET a:b=c won't work
Posted: 10 Jul 2010 07:05
by aGerman
The replacement is not needed. If you use square brackets you could split the string with this square brackets as delimiters.
words.txt
Code: Select all
[abc]
111
222
333
[###]
one
two
three
[def]
444
555
666
template.txt
batch code
Code: Select all
@echo off &setlocal
for /f "delims=[] tokens=1,2" %%a in (template.txt) do set "first=%%a" &set "second=[%%b]"
for /f "delims=:" %%i in ('findstr /n . "words.txt"') do set /a numberOfLines=%%i+1
for /f "delims=:" %%i in ('findstr /n /b /r /c:"\<\[.*\]\>" "words.txt"') do call set "sections=%%i %%sections%%"
set "sections=%numberOfLines% %sections%"
for /f "delims=:" %%i in ('findstr /n /b /c:"%second%" "words.txt"') do call set "section=%%i"
for %%i in (%sections%) do (
if %%i==%section% (
set /a startLine=%%i+1
call set /a endLine=%%before%%-1
)
set before=%%i
)
for /f "delims=: tokens=1*" %%a in ('findstr /n . "words.txt"') do (
if %%a geq %startLine% (
if %%a leq %endLine% (
echo %first%%%b
)
)
)
pause
Hope that helps.
Regards
aGerman
Re: SET a:b=c won't work
Posted: 10 Jul 2010 07:20
by espaiz
aGerman, thanks. the words.txt file will only contain marker as the first line and 10-100 words after that.
The template.txt file can a multi line, long story, e.g.:
Code: Select all
[###] little dog walked on a street and saw [###] little cats
the [###] little dog started
chasing the [###] little cats
The idea, is to use word from words.txt and replace all [###] in template.txt with this one word, then write each word to it's own file.
So if our words.txt contain:
Then a file crazy.txt would be created with the following content:
Code: Select all
crazy little dog walked on a street and saw crazy little cats
the crazy little dog started
chasing the crazy little cats
Re: SET a:b=c won't work
Posted: 10 Jul 2010 07:54
by aGerman
I see.
words.txt
Code: Select all
[abc]
111
222
333
[###]
crazy
smart
[def]
444
555
666
template.txt
Code: Select all
[###] little dog walked on a street and saw [###] little cats
the [###] little dog started
chasing the [###] little cats
batch code
Code: Select all
@echo off &setlocal
set replace=[###]
for /f "delims=:" %%i in ('findstr /n . "words.txt"') do set /a numberOfLines=%%i+1
for /f "delims=:" %%i in ('findstr /n /b /r /c:"\<\[.*\]\>" "words.txt"') do call set "sections=%%i %%sections%%"
set "sections=%numberOfLines% %sections%"
for /f "delims=:" %%i in ('findstr /n /b /c:"%replace%" "words.txt"') do call set "section=%%i"
for %%i in (%sections%) do (
if %%i==%section% (
set /a startLine=%%i+1
call set /a endLine=%%before%%-1
)
set before=%%i
)
for /f "delims=: tokens=1*" %%a in ('findstr /n . "words.txt"') do (
if %%a geq %startLine% (
if %%a leq %endLine% (
>"%%b.txt" type nul
set "word=%%b"
call :proc
)
)
)
pause
goto :eof
:proc
for /f "delims=: tokens=1*" %%a in ('findstr /n "^" "template.txt"') do (
set "line=%%b"
call :repl
)
goto :eof
:repl
if not defined line (
>>"%word%.txt" echo.
goto :eof
)
>>"%word%.txt" call echo.%%line:%replace%=%word%%%
goto :eof
This should create the text files crazy.txt and smart.txt.
Regards
aGerman
Re: SET a:b=c won't work
Posted: 10 Jul 2010 08:15
by espaiz
That works perfectly!
I'm looking at your code and it is still quite hard for me to read it :)
A question:
Because you defined "set replace=[###]", I'm not sure how to modify the script to not work in this way.
My words.txt will never contain [abc], [def], etc. It will always use the first line as the search string.
For example words.txt could be
So if you run this, it would use the "some keyword" as a search string, not [###]. This is why in my original script I tried to use first line of the words.txt
Thanks so much! It's my 12th hour trying to make this work :-)
Re: SET a:b=c won't work
Posted: 10 Jul 2010 09:33
by aGerman
12 hours? How frustrating!
words.txt
template.txt
Code: Select all
bad little dog walked on a street and saw bad little cats
the bad little dog started
chasing the bad little cats
batch code
Code: Select all
@echo off &setlocal
set /p "replace="<"words.txt"
for /f "usebackq skip=1 delims=" %%a in ("words.txt") do (
>"%%a.txt" type nul
set "word=%%a"
call :proc
)
pause
goto :eof
:proc
for /f "delims=: tokens=1*" %%a in ('findstr /n "^" "template.txt"') do (
set "line=%%b"
call :repl
)
goto :eof
:repl
if not defined line (
>>"%word%.txt" echo.
goto :eof
)
>>"%word%.txt" call echo.%%line:%replace%=%word%%%
goto :eof
Regards
aGerman
Re: SET a:b=c won't work
Posted: 10 Jul 2010 15:53
by espaiz
Yes that was frustrating, too bad it's for myself and not for a company that pay hourly haha :-)
Well, that last script is perfect, exactly what I need. Thank you so much for your help eGerman!
I have a question about the FOR loop that I posted in my first post. I put one FOR inside another one. Is this allowed? It seems that all the example of DOS scripts that I personally saw - don't use this.
Is there a website where I could read about the correct/normal structure for Batch code?
Thank you!
Re: SET a:b=c won't work
Posted: 10 Jul 2010 17:12
by aGerman
Nested FOR loops are allowed, but sometimes it is tricky to use them.
I didn't try to correct your code, because my mind was drifting to the wrong direction.
Your origin replace-line was
but
search and
replace are literal expressions in this case. You defined them as variables into the FOR loop. Thats why you have to write
!search! and
!replace!. But
old is also a variable you defined into the FOR loop.
!old:!search!=!replace!! will not work. The CALL command and double percent signs (known as "call trick") can solve the problem.
Code: Select all
call set new=%%old:!search!=!replace!%%
If there is another possibility I don't use
setLocal EnableDelayedExpansion. For your Example: If there is an exclamation mark in template.txt then it will not be echoed.
I also often use the call trick instead of
setLocal EnableDelayedExpansion and I prefer to call subroutines istead of using nested FOR loops.
Regards
aGerman
Re: SET a:b=c won't work
Posted: 10 Jul 2010 18:04
by espaiz
Using
Code: Select all
call set new=%%old:!search!=!replace!%%
Does make my code work.. but I guess it's always better to use subroutines. Thank again!
DOS is fun ;-)
Re: SET a:b=c won't work
Posted: 12 Jul 2010 20:06
by ghostmachine4
espaiz wrote:Using
DOS is fun
how about i do it with a one liner instead of all those unreadable batch code ?
Code: Select all
C:\test>more file
[###]
one
two
three
C:\test>more template
example [###]
C:\test>gawk "FNR==NR{s=$1}!/\[/{print s,$0}" template file
example one
example two
example three
Now, this is fun. for text/string data processing, use a good data processing tool like
gawk/Perl/Python.
Re: SET a:b=c won't work
Posted: 13 Jul 2010 18:55
by espaiz
ghostmachine4, yeah... one liners are cool if you are coming from perl ;-) but for some reason my output was different:
Code: Select all
gawk "FNR=NR{s=$1}!/\[/{print s,$0}" template.txt words.txt
one one
two two
three three
aGermanI ran into a problem (both your script and mine), where in template.txt, lines that contain <> tags are removed. Is there any way to fix this?
p.s. I just noticed I misspelled your nickname in my previous reply and wrote
eGerman, sorry about that! :-)