使用批处理文件查找并替换包含特殊字符的字符串

4

我在尝试让这个批处理文件正常工作时遇到了一些问题。我已经尝试了几种不同的方法来查找和替换字符串。

问题:我们必须定期更新数据库,这样做会更改一个文件。我必须将一个文件改回来以使事情正常工作。这行代码:

<network ipAdress="172.24.55.32" networkPort="9100" />

被更改为这行:

<file filename="fffff" />

需要将其更改回带有IP地址的第一行。

我尝试使用查找和替换子例程,并可以在更改“file filename”为“network ipAdress”的第一部分上使其正常工作,但第二个部分中的引号使脚本无法正常工作。以下是我拥有的代码:

@echo off
setlocal

call :FindReplace "file filename" "network ipAdress" BCPrint.XML
Timeout 2
call :FindReplace "fffff" "172.24.55.32" networkPort="9100" BCPrint.XML

exit /b

:FindReplace <findstr> <replstr> <file>
set tmp="%temp%\tmp.txt"
If not exist %temp%\_.vbs call :MakeReplace
for /f "tokens=*" %%a in ('dir "%3" /s /b /a-d /on') do (
  for /f "usebackq" %%b in (`Findstr /mic:"%~1" "%%a"`) do (
    echo(&Echo Replacing "%~1" with "%~2" in file %%~nxa
    <%%a cscript //nologo %temp%\_.vbs "%~1" "%~2">%tmp%
    if exist %tmp% move /Y %tmp% "%%~dpnxa">nul
  )
)
del %temp%\_.vbs
exit /b

:MakeReplace
>%temp%\_.vbs echo with Wscript
>>%temp%\_.vbs echo set args=.arguments
>>%temp%\_.vbs echo .StdOut.Write _
>>%temp%\_.vbs echo Replace(.StdIn.ReadAll,args(0),args(1),1,-1,1)
>>%temp%\_.vbs echo end with

我也尝试将第二部分设置为变量,例如:

set R2=172.24.55.32" networkPort="9100
call :FindReplace "file filename" "network ipAdress" BCPrint.XML
Timeout 2
call :FindReplace "fffff" "%R2%" BCPrint.XML

同时使用转义字符:

set ^R2=172.24.55.32" networkPort="9100^

我相信有一些我没有列出来的不同方法,比如不使用变量的转义字符。但我已经尽力想到的都试过了。

有没有一种方法可以用带有特殊字符的行替换另一行呢?如果能告诉我如何替换整行或第二个带引号和空格的部分,那就太好了。谢谢您的时间。

3个回答

0
要在包含“引号”的文件中更新字符串,可以使用以下解决方案。
在与您的批处理文件相同的目录中创建一个名为replace_quot.ps1的文件。将以下2行放入此脚本中。
param([string]$Name_of_File)
(Get-Content $Name_of_File) -replace '&quot;', '"' | set-content $Name_of_File

在你的批处理文件中输入下面的行:
set File_Name=File Name.txt
set "Find_String=<file filename="fffff" />"
set "Replace_String=<network ipAdress="172.24.55.32" networkPort="9100" />"

set "Find_String=%Find_String:"=&quot;%"
set "Replace_String=%Replace_String:"=&quot;%"

powershell -command "(Get-Content '%File_Name%') -replace '[\x22]', '&quot;' | Set-Content '%File_Name%'"
powershell -command "(Get-Content '%File_Name%') -replace '%Find_String%', '%Replace_String%' | Set-Content '%File_Name%'"
Powershell.exe -executionpolicy remotesigned -File replace_quot.ps1 -Name_of_File "%File_Name%"

更新File_Name的值以反映您的文件名。bat脚本将在字符串和文件中替换引号为其十六进制值。接下来,它将找到需要替换的字符串并将其恢复为所需的值。最后,它将调用replace_quot.ps1脚本以将文件的引号十六进制值更新为实际的引号字符。


0

传递包含引号的参数非常困难,因为有几个地方识别这些字符,所以每次都必须建立单独的转义序列(^)。

下面的示例脚本对于每次 call 尝试都会失败:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define argument here:
set "_ARG=R2=172.24.55.32" networkPort="9100"

setlocal EnableDelayedExpansion
rem // Echo argument string to prove that variable is set:
echo(!_ARG!
echo/

rem // Call sub-routine with delayed expansion:
call :SUB "!_ARG!"
endlocal
rem // Call sub-routine with normal (immediate) expansion:
call :SUB "%_ARG%"
rem // Call sub-routine with double normal expansion (`call`):
call :SUB "%%_ARG%%"

endlocal
exit /B


:SUB
    setlocal DisableDelayedExpansion
    rem // Delayed expansion disabled during argument expansion:
    set "ARG=%~1"
    setlocal EnableDelayedExpansion
    rem // Delayed expansion enabled for argument display (`echo`):
    echo(!ARG!
    endlocal
    endlocal
    exit /B

避免这种问题的最简单方法是将变量名传递给子过程,而不是其值:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define argument here:
set "_ARG=R2=172.24.55.32" networkPort="9100"

setlocal EnableDelayedExpansion
rem // Echo argument string to prove that variable is set:
echo(!_ARG!
echo/
endlocal

rem // Call sub-routine and pass variable name:
call :SUB _ARG

endlocal
exit /B


:SUB
    setlocal EnableDelayedExpansion
    set "#ARG=%~1"
    rem /* Normal expansion is used to get the variable name;
    rem    delayed expansion is used to read its value: */
    echo(!%#ARG%!
    endlocal
    exit /B

你好,感谢您抽出时间来查看这个问题。我该如何使用那个子程序替换文本中的部分内容并显示_Arg结果?如果将其放入我的FindReplace子程序中,它会返回!_Arg!而不是给定的值。 - Matt Roberts
这个答案只是为了演示如何将变量名传递给子程序,而不是它们的值。对于你的代码,这意味着:1. set "S2=fffff" & set "R2=172.24.55.32" networkPort="9100",2. call :FindReplace S2 R2 BCPrint.XML,3. 分别用 !%~1!!%~2! 替换 %~1%~2,4. 在 :FindReplace 后面放置 setlocal EnableDelayedExpansion,在 delexit /B 之间放置 endlocal... - aschipfl
进展了!我在第二次替换中仍然有问题。我的文件最终变成了"<network ipAdress="172.24.55.32" />"而不是"<network ipAdress="172.24.55.32" networkPort="9100" />"。奇怪的是,命令窗口显示它正在正确地执行。"在文件BCPrint.XML中用"fffff"替换为"172.24.55.32" networkPort="9100",你有什么想法吗?" - Matt Roberts
这次问题是由于批处理传递参数到临时VBScript引起的。可以通过在批处理中将所有引号加倍并在传递给VBScript时再次加倍来解决这个问题... - aschipfl

0

下载Dave Benham编写的JREPL.BAT,这是一个批处理文件/JScript混合体,可使用JScript在文件上运行正则表达式替换,并将其存储在与批处理文件相同的目录中。

@echo off
call "%~dp0jrepl.bat" "file filename=\x22fffff" "network ipAdress=\x22172.24.55.32\x22 networkPort=\x229100" /XSEQ /F BCPrint.XML /O -

\x22是十六进制形式表示的",作为一个参数字符串不能包含双引号字符。


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