如何在Windows批处理文件中替换变量内容

17
我正在编写一个简单的脚本,用于将环境变量中的文本替换为其他文本。问题在于从其他变量中获取被替换或替换的文本时会出现困难。
SET a=The fat cat
ECHO %a%
REM Results in 'The fat cat'
ECHO %a:fat=thin%
REM Results in 'The thin cat'

正常运行(输出为“The fat cat”和“The thin cat”)

然而,如果“fat”或“thin”是变量,则无法正常工作。

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!'

我知道这是可以实现的,因为我以前在博客中看到过,但我没有保存博客的链接。
有人有什么想法吗?
附注:我正在运行Windows 7上的脚本。
附注二:我知道这在Perl/Python/其他脚本语言中更容易,但我只想知道为什么应该很容易的东西不是立即显而易见的。
附注三:我也尝试了开启延迟扩展的脚本。
SETLOCAL enabledelayedexpansion

这没有任何区别。
7个回答

14
请尝试以下方法: 将代码复制并粘贴到记事本中,然后另存为批处理文件。
   @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%]

我希望你已经被说服了!


1
但是当你的代码在FOR /F循环内部时,这种方法不起作用。%f% - 什么也不返回。 - NoGotnu

11

使用CALL命令。将以下内容放入批处理脚本中并运行:

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

根据此处所述,我们使用以下事实:

CALL internal_cmd

...

internal_cmd运行一个内部命令,并首先展开参数中的任何变量

在我们的情况下,internal_cmd最初是set a=%%a:%b%^=%c%%%

在展开后,internal_cmd变成了set a=%a:fat=thin%

因此,在我们的情况下,运行:

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

等同于运行:

set a=%a:fat=thin%


2

问题所在:

echo %a:%c%=thin%

它试图扩展两个变量:a:=thin,它们之间带有一个c常量字符串。

试试这个:

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

第一条命令输出:

echo %a:fat=thin%

这将被导入第二个命令行进行评估。


1
我会期望这种行为,但我也希望有一些延迟扩展或“嵌套”扩展的方法。顺便说一句,我同意,在DOS中没有什么是容易的,也不是立即显而易见的。 - GKelly

2

我尽量避免使用 SETLOCAL enabledelayedexpansion ... ENDLOCAL,因为通常我想要设置或修改一些变量,并且希望新值在脚本的其他区域或批处理脚本结束后仍然可用(SETLOCAL|ENDLOCAL 将忘记“SETLOCAL”部分中任何新变量或变量更改。有时这很方便,但对我来说,通常不是。

目前我使用@Zuzel描述的方法,但在我知道这个方法之前,我曾经使用过这个方法,它非常相似(但看起来有点更加复杂):

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

示例脚本:

@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

运行脚本后的输出结果:
before: "The fat cat"
replace "fat" with "thin" in "The fat cat" using for:
after for:  "The thin cat"

我喜欢这种方法,因为你可以使用修改后的变量调用外部程序(或内部命令),并捕获和处理命令的输出(逐行)。

但是,在大多数情况下,包括您描述的情况下,Zuzel的方法更简单、更干净。

注意:

这两种方法(当然还有SETLOCAL enabledelayedexpansion ... ENDLOCAL)只有在批处理脚本内运行时才能正确工作。如果您尝试直接在命令提示符窗口中使用这两种方法(“call”或“for”),则得到的结果与从脚本运行的输出不同。

例如,将此作为脚本运行:

@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%")

运行脚本后的输出结果:
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")

现在,请直接在命令提示符窗口中运行这些命令:
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:\>

检查命令提示符窗口中的前后输出行:

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")

请注意,替换是正确的,但是请注意,在直接在命令提示符窗口运行这些命令时,每次进行替换时都会在预期值之前和之后添加一组“%”(百分号)。因此,直接在命令提示符窗口中测试任何这些方法都会变得更加困难。


1

最近我遇到了相同的情况..如前所述,我使用下面的方法并且顺利解决了问题...

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

输出:

\log\process

它起作用了..!!


1

那么……这个怎么样?

@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%]

很棒,是吧?


-1

:: 在%var%上使用perl =~ 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% 

输出:

before=The fat cat
after=The thin cat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接