3

I am trying to create a small script to do light template replacement duties, but I'm getting stuck with dereferencing a variable the way I want.

Here's my template replacement batch file:

@echo off

echo ------------
echo %~nx0%

SETLOCAL ENABLEDELAYEDEXPANSION

SET "src=%~1"
SET "dst=%~2"

ECHO %src%
FOR /F "tokens=1,2* delims=%%" %%i IN (%src%) DO (

   IF "%%k"=="" (
      ECHO %%i >> %dst%
   ) ELSE (
      SET "first=%%i"
      SET "middle=%%j"
      SET "last=%%k"
      SET "replace=!middle:~2,-1!"

      IF NOT "!first:~-1!"=="<" (
         ECHO %%i >> %dst%
      ) ELSE IF NOT "!middle:~0,1!"=="=" (
         ECHO %%i >> %dst%
      ) ELSE IF NOT "!last:~0,1!"==">" (
         ECHO %%i >> %dst%
      ) ELSE (
         ECHO !first:~0,-1! ^<^< !replace! ^>^> !last:~1!
      )
   )
)

ENDLOCAL

GOTO :EOF

:Error
EXIT /B 1

an example input file might look like:

{
    "name":                     "<%= comp.name %>",
    "version":                  "<%= comp.version %>",
    "description":              "<%= comp.description %>",
    "author":                   "me",
    "url":                      "https://localhost/<%= comp.name %>"
}

and an example call might look like:

SET comp.name=TestApp
SET comp.version=1.0
SET comp.description=The most awesome thing you will ever see
CALL TemplateReplacement.bat %1 %2

example output I want to see would be:

{
    "name":                     "TestApp",
    "version":                  "1.0",
    "description":              "The most awesome thing you will ever see",
    "author":                   "me",
    "url":                      "https://localhost/TestApp"
}

when I get into the ELSE, I get output like:

    "name":                     " << comp.name >> ",
    "version":                  " << comp.version >> ",
    "description":              " << comp.description >> ",

(note: I'm purposefully echoing the replacement situation to the console vs. the file since I'm debugging; that is reflected in the output above).

!replace! is correct; it's the name of a variable I want to expand, so I tried !!replace!!, since I expected !replace! to dump something like comp.name and then !comp.name! would resolve to TestApp (and since I want it resolving at execute time, this feels like the proper syntax to be sniffing at). that is not what happens, however -- instead I just get comp.name, etc. I have now gone through every iteration of replace I can think to try (e.g. %!replace!%, !!replace!! and nonsensical ones like %%replace%%, !%replace%! and !!!replace!!!) but nothing jives. ECHOing !comp.name! from within the FOR shows that the variable can be resolved (as does %comp.name%) so I'm extremely confused why !!replace!! wouldn't be the proper thing to do here.

is there a trick that I'm missing?

p.s. while alternate scripts that will solve the problem I'm trying to solve are certainly welcome, I am interested in knowing why I cannot solve it this way.

rguilbault
  • 553
  • 1
  • 5
  • 17
  • 1
    You can not perform a _double_ delayed expansion like `!!this!!` because the parsing of such feature is done _from left to right_. I suggest you to read [this post](http://stackoverflow.com/questions/10166386/arrays-linked-lists-and-other-data-structures-in-cmd-exe-batch-script/10167990#10167990), where this management is explained. – Aacini Oct 18 '16 at 00:26
  • @Aacini - I could have sworn there was some syntax along the lines of `call %!this!%` that would let you get an additional layer of delayed expansion without using a `for /f` loop, but I can't remember exactly what it is. – SomethingDark Oct 18 '16 at 01:19
  • 1
    @SomethingDark, are you talking about `call %%!this!%%`? – aschipfl Oct 18 '16 at 10:24
  • So, besides replacing the `comp.` variables, the purpose of your script is also to replace `<%=` by _space_+`<<` and `%>` by `>>`+_space_, is that correct? – aschipfl Oct 18 '16 at 11:38
  • no, that << >> stuff was just for debug output so I could see the separate parts of the line. the idea is that the <%= var %> would simply be replaced, so the total output for the name line, for example would be: "name": "TestApp", – rguilbault Oct 18 '16 at 13:31
  • @Aacini - thank you for that link. that explains why I cannot use !!replace!! and why I was seeing various things in the output that looked like it was no longer processing the variable (like the substring params). so I guess that means I need to get the value of !replace! into a %-addressable or %%-addressable variable? – rguilbault Oct 18 '16 at 14:16
  • I got it!! @Aacini -- if you want to suggest this (or your preferred snippet) to me, I will accept your answer since your comment led me to resolve my issue: FOR /F %%a IN ("!middle:~2,-1!") DO SET replace=!%%a! – rguilbault Oct 18 '16 at 14:19
  • I posted my answer below. Of course, you may also upvote the answer at that link! **`;)`** – Aacini Oct 18 '16 at 17:28
  • 1
    @SomethingDark: err..., no. There is no way to perform a double delayed expansion _in the same command_, that is, whitout use an additional `for` command. You may review some examples of extreme use of the multiple expansion phases in [split string into substrings based on delimiter](http://www.dostips.com/forum/viewtopic.php?f=3&t=6429) thread. For example, look for the "Replace each substring by a series of different strings" one. – Aacini Oct 18 '16 at 17:53

2 Answers2

2

This type of management is explained at this answer; for example, you may use anyone of these methods:

CALL SET replace=%%!middle:~2,-1!%%

FOR /F %%a IN ("!middle:~2,-1!") DO SET replace=!%%a!

The last line run faster than the former...

Community
  • 1
  • 1
Aacini
  • 65,180
  • 12
  • 72
  • 108
0

This should work with your test file and may help you with your specific issue:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

SET SRC=%~1
SET DST=%~2

(FOR /F "TOKENS=1,2* DELIMS=<>" %%A IN ('FINDSTR/R "%%.*%%" "%SRC%"') DO (
    SET PRE=%%B
    CALL ECHO=%%A%%!PRE:~3,-2!%%%%C))>%DST%
Compo
  • 36,585
  • 5
  • 27
  • 39
  • it works for very simple quoted values, as I showed in my example input, but if you have something like: "url": "https://localhost/<%= comp.name %>/" it causes corruption (really just outputs unresolved symbol stuff like ",-3". additionally, this omits lines that don't have a replacement to be made (I grant that I did not include these details in the OP, but I was trying not to clutter things). – rguilbault Oct 18 '16 at 13:35
  • That's why I said only that it works with the test file provided. I cannot possibly guess every file you are ever going to run through the script nor will I attempt to provide a more robust solution to do so. Your question was about how to do the expansion and my example does that. Your task is to transfer my solution to your future implementation. – Compo Oct 18 '16 at 14:58