5

We have a text file that lists a bunch of paths, and a batch file that reads the lines from this file.

For instance, TargetFolders.txt might contain the line:

%ProgramFiles%\Acme\FooBar %VersionNumber%

Naturally, when we read this line from the text file (using a FOR command), the variable %%I receives the actual line text, with the % signs rather than replacing the variable values. So,

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    echo Folder: %%I
)

Prints

Folder: %ProgramFiles%\Acme\FooBar %VersionNumber%

How does one make it replace the actual variable values, so that it prints

Folder: C:\Program Files\Acme\FooBar 7.0

?

Irfy
  • 9,323
  • 1
  • 45
  • 67
System.Cats.Lol
  • 1,620
  • 1
  • 26
  • 47
  • Seems like a duplicate of [How expand a CMD shell variable twice (recursively)](http://stackoverflow.com/questions/1199931/how-expand-a-cmd-shell-variable-twice-recursively) – Irfy Feb 20 '12 at 23:22
  • Hmmm it's not an exact duplicate because you have the `%...%` in place, but the link may be helpful... – Irfy Feb 20 '12 at 23:27
  • lrfy: Not exactly; I'm not talking about multiple expansions, just getting it to reference the variable in the first place rather than the %name%. – System.Cats.Lol Feb 20 '12 at 23:27

3 Answers3

8
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
    for /F "usebackq delims=" %%J in (`echo %%I`) do echo Folder: %%J
)

There you go. (This is what you wanted, right?)

mklement0
  • 382,024
  • 64
  • 607
  • 775
Irfy
  • 9,323
  • 1
  • 45
  • 67
  • lrfy: Not sure I follow you. How does this solve the original issue of not being able to reference environment variables within the TXT file? So that for instance the text file can say %ProgramFiles% and the batch file prints out C:\Program Files. – System.Cats.Lol Feb 20 '12 at 23:38
  • The 'echo %AAA%' part gives you the twice-evaluated string, but since it contains spaces, I guess the loop tries to iterate over the individual parts... And I can't seem to fix that. – Irfy Feb 20 '12 at 23:41
  • Thanks, The backquote option is what was needed, to treat the "echo %%I" output as a command line so that the %variables% would be properly recognized. – System.Cats.Lol Feb 20 '12 at 23:52
  • You have an unwanted extra `=` when defining delims for %%J loop. Also, this solution will not work if a folder contains an unquoted special character. See [my answer](http://stackoverflow.com/a/9370376/1012053). Your use of an extra FOR with ECHO is better than my use of CALL because it doesn't mangle quoted caret `"^"` – dbenham Feb 21 '12 at 00:26
  • 1
    Actually, neither is good with caret. FOR/ECHO mangles unquoted `^` whereas CALL mangles quoted `"^"`. :( – dbenham Feb 21 '12 at 00:52
  • 3
    OT: I think we sort of implicitly agree here that NT shell programming is a horror in itself, compared to the linux/unix shells... – Irfy Feb 21 '12 at 21:58
6

Simply adding a CALL may solve your problem. (also your definition of VersionNumber was wrong)

SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  call echo Folder: %%I
)

But this will fail if your file contains unquoted special characters like &, >, <, |.

For example, the following line would fail:

%ProgramFiles%\This&That\ %VersionNumber%

It will work if it is quoted

"%ProgramFiles%\This&That\" %VersionNumber%

The CALL will also mangle any quoted carets: "^" will become "^^"

The best solution would be to modify your text file and replace each % with !.

!ProgramFiles!\Acme\FooBar !VersionNumber!
!ProgramFiles!\This&That !VersionNumber!

Now you can safely use delayed expansion to expand the variables within the loop.

setlocal enableDelayedExpansion
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)

If your text file already has ! that you want to preserve, then it must be escaped. Also ^ will have to be escaped if it appears on a line with a !.

preserve caret ^^ and exclamation ^! by escaping
caret ^ without exclamation is no problem

Alternatively you can substitute variables for caret and exclamation literals

alternate method to preserve caret !c! and exclamation !x!
caret ^ without exclamation still no problem

And then define the variables in your batch

setlocal enableDelayedExpansion
set "x=^!"
set "c=^"
SET VersionNumber=7.0
FOR /F "eol=; delims=" %%I IN (TargetFolders.txt) DO (
  echo Folder: %%I
)
dbenham
  • 127,446
  • 28
  • 251
  • 390
0

To complement the existing helpful answers with a more robust variant that also handles the following embedded characters correctly: & | < > ^[1] :

Note that for simplicity I'm using a string to drive the outer loop, whose literal value - to be expanded later by the inner loop - is %ProgramFiles%\Acme\FooBar %VersionNumber%, due to the doubled % instances.

set "VersionNumber=0.7"
for /f "delims=" %%f in ("%%ProgramFiles%%\Acme\FooBar %%VersionNumber%%") do (
  for /f "delims=" %%g in ('echo "%%f"') do echo Folder: [%%~g]
)

This yields something like Folder: [C:\Program Files\Acme\FooBar 0.7]

Note:

  • By default, for /f interprets a single-quoted string ('...') as a command to execute and whose output to capture; delims= tells for to capture the output as a whole in a single variable.
    (While you can use usebackq with a `...`-enclosed command instead, there's no advantage to doing so in this case.)
  • Note how the reference to the outer loop's variable is double-quoted ("%%f"), which is what allows the value to contain said special characters without breaking the echo command.

  • Because the output will then also be double-quoted, the ~ operator is used to strip the enclosing double quotes from the captured value on echoing it (%%~g).


[1] A solution that additionally handles embedded " instances is trickier:

  rem Construct a variable whose value contains all shell metacharacters.
  rem % chars. meant to be treated as literals must be doubled (%%)
  rem Note the extra " at the end, which appends an unbalanced double quote. 
set "var=%%OS%% & %%ba^r | (baz) <mo'>""

  rem Embedded " chars. must be escaped as "" (doubling them).  
for /f "delims=" %%v in ('echo "%var:"=""%"') do set "expandedVar=%%~v"

  rem Note: The resulting variable's value: 
  rem  - can only be echoed *double-quoted*, as the command will otherwise break.
  rem  - still contains the *doubled* embedded double quotes, which, however,
  rem    is the escaping that other Microsoft programs expect.
echo "[%expandedVar%]"

This yields: "[Windows_NT & %ba^r | (baz) <mo'>""]"

mklement0
  • 382,024
  • 64
  • 607
  • 775