I want to use a for loop to replace only the 2nd match of a string in a file. To achieve this I use a PowerShell CLI command which has worked outside the for
loop.
Now since I have to iterate over multiple files defined in %files%
I use for /f "tokens=1"
to separate file 1 from file 2 because both strings I want to replace are in file 1.
This is the for
loop I am using:
for /l %%a in (1,1,2) do (
for /f "tokens=1" %%b in ("!files!") do (
set "file=%apb%\%%~b"
echo !string[%%a]! --^> !replace[%%a]!
rem Use PowerShell to replace only the second occurrence of a match, here `$matches[1]`.
PowerShell -NoLogo -NoProfile -Command ^
"$file = '!file!';" ^
"$string = '(?m)^!string[%%a]!=.*';" ^
"$content = Get-Content -Raw -Encoding UTF8 $file;" ^
"$matches = [regex]::Matches($content, $string);" ^
"if ($matches.Count -ge 2) {" ^
" $stringTwo = $matches[1];" ^
" $stringTwoPosition = $stringTwo.Index;" ^
" $stringTwoLength = $stringTwo.Length};" ^
"Set-Content -NoNewLine $file -Value $content.Remove($stringTwoPosition, $stringTwoLength).Insert($stringTwoPosition, '!string[%%a]!=!string[%%a]!')"
)
)
My issue with the for
loop is, that it returns the following error:
The string is missing the terminator: '.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
All terminators are set though. I have read somewhere that expanded variables may cause issues withing PowerShell CLI commands but I have no idea how to bypass expanding the variables and getting the result I want to achieve.
I have tried calling the values of !string[%%a]!
and !replace[%%a]!
through non-expanded variables %stringAlias%
and %replaceAlias%
which did not work.
This is the rest of the snippet to prepare for the final for
loop:
@echo off
setlocal EnableDelayedExpansion
rem Set target files.
set "files=Engine\Config\BaseEngine.ini APBGame\Config\DefaultEngine.ini"
rem Initialize variables.
set "replace="
set "string="
call :subroutine.ping_optimization "%files%"
if !errorlevel! equ 0 (
set "count=1"
for %%a in (
"1.0"
"5.0"
rem ...
) do (
set "replace[!count!]=%%~a"
set /a "count+=1"
)
)
if !errorlevel! equ 1 (
set "count=1"
for %%a in (
"0.001"
"0.001"
rem ...
) do (
set "replace[!count!]=%%~a"
set /a "count+=1"
)
)
set "count=1"
for %%a in (
"AckTimeout"
"KeepAliveTime"
rem ...
) do (
set "string[!count!]=%%~a"
set /a "count+=1"
)
rem for loop causing issues goes here...
One of the many unfortunate behaviors of cmd.exe
(the interpreter of batch files) is that commands behave differently inside the (...)
-enclosed bodies of for /f
loops.
Specifically, the intra-line ^
character in "$string = '(?m)^!string[%%a]!=.*';" ^
- even though it is inside an "..."
string - is inexplicably not used verbatim and requires escaping as ^^
.
That is, replacing the line
"$string = '(?m)^!string[%%a]!=.*';" ^
in the code in your question with
"$string = '(?m)^^!string[%%a]!=.*';" ^
should fix your problem.