简体   繁体   中英

How to substitute variable contents in a Windows batch file

I'm writing a simple script to substitute text in an environment variable with other text. The trouble I get is with the substituted or substituted text being pulled from other variables

SET a=The fat cat
ECHO %a%
REM Results in 'The fat cat'
ECHO %a:fat=thin%
REM Results in 'The thin cat'

Works fine (output is 'The fat cat' and 'The thin cat'

However, if 'fat' or 'thin' are in variables, it doesn't work

SET b=fat
ECHO %a:%c%=thin%
REM _Should_ give 'The thin cat'.
REM _Actually_ gives '%a:fat=thin%' (the %c% is evaluated, but no further).

REM using delayed evaluation doesn't make any difference either
ECHO !a:%c%=thin!
REM Actual output is now '!a:fat=thin!'

I know this can be done as I've seen it in blogs before, but I never saved the link to the blogs.

Anyone have any ideas?

PS. I'm running the scripts on Windows 7

PPS. I know this is easier in Perl/Python/other script language of choice, but I just want to know why something that should be easy is not immediately obvious.

PPPS. I've also tried the scripts with delayed expansion explicitly turned on

SETLOCAL enabledelayedexpansion

This makes no difference.

Please try the following:

Copy and paste the code into Notepad and save it as a batch file.

   @echo off
   setlocal enabledelayedexpansion

   set str=The fat cat
   set f=fat

   echo.
   echo          f = [%f%]

   echo.
   echo        str = [%str%]

   set str=!str:%f%=thin!

   echo str:f=thin = [%str%]

I hope you're convinced!

Use CALL . Put the following in a batch script and run it:

set a=The fat cat
set b=fat
set c=thin

REM To replace "%b%" with "%c%" in "%a%", we can do:
call set a=%%a:%b%^=%c%%%
echo %a%
pause

As stated here , we use the fact that:

CALL internal_cmd

...

internal_cmd Run an internal command, first expanding any variables in the argument .

In our case internal_cmd is initially set a=%%a:%b%^=%c%%% .

After expansion internal_cmd becomes set a=%a:fat=thin% .

Thus, in our case running

call set a=%%a:%b%^=%c%%%

is equivalent to running:

set a=%a:fat=thin% .

The problem with:

echo %a:%c%=thin%

is that it tries to expand two variables: a: and =thin with a c constant string between them.

Try this:

echo echo ^%a:%c%=thin^% | cmd

The first command outputs:

echo %a:fat=thin%

which is piped into a second command shell for evaluation.

And... How about this?

@echo off
setlocal enabledelayedexpansion

set str=The fat cat
set f=fat
set t=thin

echo.
echo       f = [%f%]
echo       t = [%t%]

echo.
echo     str = [%str%]

set str=!str:%f%=%t%!

echo str:f=t = [%str%]

Nifty eh?

Recently I came accross the same situation..As said earlier, I used like below and worked...

set filearg=C:\data\dev\log\process
set env=C:\data\dev

REM I wanted \log\process as output

SETLOCAL enabledelayedexpansion
set str2=!filearg:%env%=!
echo Output : %str2%
echo.
endlocal

Output :

\log\process

It worked..!!

I try to avoid using SETLOCAL enabledelayedexpansion ... ENDLOCAL all (or nearly all) the time, because usually I want to set or modify a few variables and I want the new values to be available in other areas of the script or after the batch script ends (SETLOCAL|ENDLOCAL will forget about any new variables or changes to variables in the "SETLOCAL" part of the script. Sometimes that's handy, but for me I find it's usually not.

Currently I use the method described by @Zuzel , but before I knew about that method, I used to use this, which is very similar (but looks a bit more complicated):

for /F "usebackq delims=" %%f in (`echo set "a=^%a:%b%=%c%^%"`) do @%%f

example script:

@echo off

set a=The fat cat
set b=fat
set c=thin

@echo.
@echo before: "%a%"

@echo replace "%b%" with "%c%" in "%a%" using for:
for /F "usebackq delims=" %%f in (`echo set "a=%%a:%b%=%c%%%"`) do @%%f
@echo after for:  "%a%"

goto :EOF

the output from running the script:

before: "The fat cat"
replace "fat" with "thin" in "The fat cat" using for:
after for:  "The thin cat"

I like this method because you can call external programs (or internal commands) using modified variables and also capture and process the output of the command (line by line).

But, Zuzel's method is simpler and cleaner for most situations, including the one you described.

Note:

Both of these methods (and also SETLOCAL enabledelayedexpansion ... ENDLOCAL , of course), only work correctly if run from within a batch script. If you try to use either of these two methods ("call" or "for") directly in a command prompt window, you will get something different from what the output was running from a script.

For example, run this as a script:

@echo off

set a=The fat cat
set b=fat
set c=thin
set d=calico
set e=sleepy

@echo.
@echo before: "%a%"
@echo.

@echo replace "%b%" with "%c%" in "%a%" using call:
call set a=%%a:%b%=%c%%%
@echo after call: "%a%" ("%b%" to "%c%")

@echo.
@echo replace "%c%" with "%d%" in "%a%" using for:
for /F "usebackq delims=" %%f in (`echo set "a=%%a:%c%=%d%%%"`) do @%%f
@echo after for:  "%a%" ("%c%" to "%d%")

@echo.
@echo replace "%d%" with "%e%" in "%a%" using call:
call set a=%%a:%d%=%e%%%
@echo after call: "%a%" ("%d%" to "%e%")

the output from running the script:

before: "The fat cat"

replace "fat" with "thin" in "The fat cat" using call:
after call: "The thin cat" ("fat" to "thin")

replace "thin" with "calico" in "The thin cat" using for:
after for:  "The calico cat" ("thin" to "calico")

replace "calico" with "sleepy" in "The calico cat" using call:
after call: "The sleepy cat" ("calico" to "sleepy")

Now, run those commands directly in a command prompt window:

c:\>
c:\>set a=The fat cat

c:\>set b=fat

c:\>set c=thin

c:\>set d=calico

c:\>set e=sleepy

c:\>
c:\>@echo.


c:\>@echo before: "%a%"
before: "The fat cat"

c:\>@echo.


c:\>
c:\>@echo replace "%b%" with "%c%" in "%a%" using call:
replace "fat" with "thin" in "The fat cat" using call:

c:\>call set a=%%a:%b%=%c%%%

c:\>@echo after call: "%a%" ("%b%" to "%c%")
after call: "%The thin cat%" ("fat" to "thin")

c:\>
c:\>@echo.


c:\>@echo replace "%c%" with "%d%" in "%a%" using for:
replace "thin" with "calico" in "%The thin cat%" using for:

c:\>for /F "usebackq delims=" %f in (`echo set "a=%%a:%c%=%d%%%"`) do @%f

c:\>@echo after for:  "%a%" ("%c%" to "%d%")
after for:  "%%The calico cat%%" ("thin" to "calico")

c:\>
c:\>@echo.


c:\>@echo replace "%d%" with "%e%" in "%a%" using call:
replace "calico" with "sleepy" in "%%The calico cat%%" using call:

c:\>call set a=%%a:%d%=%e%%%

c:\>@echo after call: "%a%" ("%d%" to "%e%")
after call: "%%%The sleepy cat%%%" ("calico" to "sleepy")

c:\>

examine the before and after output lines from the command prompt window:

before: "The fat cat"

replace "fat" with "thin" in "The fat cat" using call:
after call: "%The thin cat%" ("fat" to "thin")

replace "thin" with "calico" in "%The thin cat%" using for:
after for:  "%%The calico cat%%" ("thin" to "calico")

replace "calico" with "sleepy" in "%%The calico cat%%" using call:
after call: "%%%The sleepy cat%%%" ("calico" to "sleepy")

Notice that the substitutions are made correctly, but also notice that with running these commands directly in the command prompt window, it adds a set of "%" (percent signs) before and after the expected value each time the substitution is made. So, it makes it difficult to test any of these methods directly in the command prompt window.

:: Use perl on %var% =~ s/$old/$new/

set var=The fat cat
set old=fat
set new=thin

ECHO before=%var%

for /f "delims=" %%m in ('echo %var% ^|perl -pe "s/%old%/%new%/" ') do set var=%%m

ECHO after=%var% 

Output:

before=The fat cat
after=The thin cat

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM