Как заменить содержимое переменной в пакетном файле Windows

Я пишу простой script для замены текста в переменной среды другим текстом. Проблема, которую я получаю, заключается в замене замещенного или замещенного текста на другие переменные

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

Прекрасно работает (вывод "Толстый кот" и "Тонкая кошка"

Однако, если "жир" или "тонкий" находятся в переменных, он не работает

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

Я знаю, что это можно сделать, как я видел это в блогах раньше, но я никогда не сохранял ссылку на блоги.

У кого-нибудь есть идеи?

PS. Я запускаю сценарии в Windows 7

ПФС. Я знаю, что это проще в Perl/Python/другом языке script, но я просто хочу знать, почему что-то, что должно быть легко, не сразу становится очевидным.

PPPS. Я также пробовал сценарии с замедленным расширением, явно включенным

SETLOCAL enabledelayedexpansion

Это не имеет значения.

Ответ 1

Попробуйте следующее:

Скопируйте и вставьте код в блокнот и сохраните его в виде пакетного файла.

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

Надеюсь, ты убежден!

Ответ 2

Используйте CALL. Поместите следующее в пакетном режиме script и запустите его:

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 изначально устанавливает a = %% a:% b% ^ =% c %%%.

После расширения inner_cmd становится установлено a =% a: fat = thin%.

Таким образом, в нашем случае работает

набор вызовов a = %% a:% b% ^ =% c %%%

эквивалентно запуску:

установите a =% a: fat = thin%.

Ответ 3

Проблема с:

echo %a:%c%=thin%

заключается в том, что он пытается развернуть две переменные: a: и =thin с константой c между ними.

Попробуйте следующее:

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

Первая команда выдает:

echo %a:fat=thin%

который передается во вторую командную оболочку для оценки.

Ответ 4

И... Как насчет этого?

@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?

Ответ 5

Недавно я столкнулся с той же ситуацией... Как было сказано ранее, я использовал, как показано ниже, и работал...

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

Это сработало..!!

Ответ 6

Я стараюсь не использовать SETLOCAL enabledelayedexpansion ... ENDLOCAL все (или почти все) время, потому что обычно я хочу установить или изменить несколько переменных, и я хочу, чтобы новые значения были доступны в других областях script или после пакетный script заканчивается (SETLOCAL | ENDLOCAL забудет о любых новых переменных или изменениях в переменных в "SETLOCAL" части script. Иногда это удобно, но для меня это обычно не так.

В настоящее время я использую метод, описанный @Zuzel, но прежде чем я узнал об этом методе, я использовал его, что очень похоже (но выглядит немного сложнее):

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

Пример 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

вывод из запуска script:

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

Мне нравится этот метод, потому что вы можете вызывать внешние программы (или внутренние команды) с помощью модифицированных переменных, а также захватывать и обрабатывать вывод команды (строка за строкой).

Но метод Zuzel проще и чище для большинства ситуаций, включая тот, который вы описали.

Примечание:

Оба эти метода (а также SETLOCAL enabledelayedexpansion ... ENDLOCAL, конечно) работают корректно, если они запускаются из пакета script. Если вы попытаетесь использовать любой из этих двух методов ( "вызов" или "для" ) непосредственно в окне командной строки, вы получите что-то отличное от того, что вывод был запущен из script.

Например, запустите это как 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%")

вывод из запуска 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")

Теперь запустите эти команды непосредственно в окне командной строки:

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

Обратите внимание, что подстановки сделаны правильно, но также замечают, что при запуске этих команд непосредственно в окне командной строки он добавляет набор "%" (знаки процента) до и после ожидаемого значения каждый раз, когда выполняется подстановка, Таким образом, это затрудняет проверку любого из этих методов непосредственно в окне командной строки.

Ответ 7

:: Использовать 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% 

Вывод:

before=The fat cat
after=The thin cat