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:

Code: Select all

[###]
one
two
three


A template.txt file that looks like:

Code: Select all

example [###]


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

Code: Select all

example [###]



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:

Code: Select all

[###]
crazy


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

Code: Select all

some keyword
crazy
smart


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

Code: Select all

bad
crazy
smart



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

Code: Select all

set new=!old:search=replace!

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


aGerman
I 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! :-)