我有一个文本文件,我想将注释的字符串更改为其他值,反之亦然。文本文件中的注释字符串以感叹号(!)开头。我正在使用以下文章中提到的FindReplace函数: Batch script to find and replace a string in text file within a minute for files upto 12 MB
当我使用带有包含!的字符串的:FindReplace函数时,我怀疑delayedExpansion正试图解释!作为变量的一部分,因此当调用FindReplace时,文件中缺少整行。我试图逃避!在字符串中使用^或^^,它似乎不起作用。
setlocal enableDelayedExpansion
REM //bunch of other script
REM . . . [rest of script not shown]
set /p user_name="Enter user name: %=%"
call :FindReplace "username :" "username: %user_name%" tmpfile.cfg
REM Comments in txt file start with an !
call :FindReplace "!This is a comment" "This is no longer a comment" tmpfile.cfg
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 (
<%%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
如何才能正常运作?
编辑(1): 看起来我需要基于以下“if”语句的“enableDelayedExpansion”。如果我将“enableDelayedExpansion”注释掉,则“if”块不会执行。
IF EXIST "%PROD_PATH%\%OS_VER%\bin\prod.exe" (
ECHO There appears to be an installation of PROD in %PROD_PATH%
SET /p overwrite_input="Would you like to overwrite this installation? [y/n] %=%"
IF "%overwrite_input%" == "n" (
ECHO Installation Terminating...
ECHO -----------------------------------------------------------------------------------------------
PAUSE
EXIT /B
) ELSE IF "%overwrite_input%" == "y" (
ECHO Uninstalling existing application...
call :Uninstall "%PROD_PRE_COPY%"
ECHO Continuing installation...
ECHO -----------------------------------------------------------------------------------------------
) ELSE (
ECHO Error: Operation Invalid. Please enter 'y' for yes or 'n' for no without quotes
ECHO Installation Terminating...
ECHO -----------------------------------------------------------------------------------------------
PAUSE
EXIT /B
)
)
答案 0 :(得分:3)
奇怪的行为的原因是({1}},%1
,%2
等(子程序)参数在很早的状态下被解析,早在延迟变量扩展之前很久完成(实际上即使在立即扩展之前)。
要克服这个问题,您需要避免将字符串/值作为参数传递给子例程。我看到以下选项:
在调用子例程之前,需要将子程序要处理的字符串/值分配给全局变量。这些在延迟扩展的子程序中读取:
%3
在调用子例程之前,需要将子程序要处理的字符串/值分配给变量。然后将变量的名称作为参数传递给子例程。使用延迟扩展间接读取子程序中的变量:
setlocal EnableDelayedExpansion
rem ...SKIPPING SOME CODE...
rem Define global variables and read them in subroutine:
set "strFind=!This is a comment"
set "strRepl=This is no longer a comment"
set "fileTmp=tmpfile.cfg"
call :FindReplace
endlocal
exit /b
:FindReplace
set "tmpf=%temp%\tmp.txt"
If not exist %"temp%\_.vbs" call :MakeReplace
for /F "tokens=*" %%a in ('dir /S /B /A:-D /O:N "%fileTmp%"') do (
for /F "usebackq" %%b in (`Findstr /MIC:"!strFind!" "%%a"`) do (
<%%a cscript //nologo %temp%\_.vbs "!strFind!" "!strRepl!">"%tmpf%"
if exist "%tmpf%" move /Y "%tmpf%" "%%~dpnxa">nul
)
)
del %temp%\_.vbs
exit /b
rem ...SKIPPING SOME CODE...
注意:强>
要了解命令提示符setlocal EnableDelayedExpansion
rem ...SKIPPING SOME CODE...
rem Define variables and pass their names to the subroutine:
set "strFind=!This is a comment"
set "strRepl=This is no longer a comment"
set "fileTmp=tmpfile.cfg"
call :FindReplace strFind strRepl fileTmp
endlocal
exit /b
:FindReplace
set "tmpf=%temp%\tmp.txt"
If not exist %"temp%\_.vbs" call :MakeReplace
for /F "tokens=*" %%a in ('dir /S /B /A:-D /O:N "!%~3!"') do (
for /F "usebackq" %%b in (`Findstr /MIC:"!%~1!" "%%a"`) do (
<%%a cscript //nologo %temp%\_.vbs "!%~1!" "!%~2!">"%tmpf%"
if exist "%tmpf%" move /Y "%tmpf%" "%%~dpnxa">nul
)
)
del %temp%\_.vbs
exit /b
rem ...SKIPPING SOME CODE...
如何解析您可能对this great thread感兴趣的脚本。
答案 1 :(得分:2)
将您的setlocal行更改为
setlocal DISABLEDELAYEDEXPANSION
它应该有用。
成功逃脱!你必须记住,shell会传递每个命令两次并且每次都会进行一次unescapes。所以回应一个!你必须这样做
echo ^^!
但是,如果你分配!对于一个var而后来回应它你还需要另一个^:
set x=this works^^^!
echo %x%
答案 2 :(得分:2)
REM . . . [rest of script not shown]
SETLOCAL DISABLEDELAYEDEXPANSION
set /p user_name="Enter user name: %=%"
call :FindReplace "username :" "username: %user_name%" tmpfile.cfg
REM Comments in txt file start with an !
call :FindReplace "!This is a comment" "This is no longer a comment" tmpfile.cfg
ENDLOCAL
如所示定位的其他setlocal disabledelayedexpansion
/ endlocal
括号应该可以解决问题。
答案 3 :(得分:0)
setlocal enableDelayedExpansion
REM //bunch of other script
REM . . . [rest of script not shown]
set /p user_name="Enter user name: %=%"
call :FindReplace "username :" "username: %user_name%" tmpfile.cfg
REM Adding this section, as it may be pertinent to the DelayedExpansion
echo Select your destination:"
echo 1) local
echo 2) Far
set /p menu_selection="selection? %=%"
if "!selection!" == "2" (
set "advancedOpt=1"
)
REM Comments in txt file start with an !
call :FindReplace "!This is a comment" "This is no longer a comment" tmpfile.cfg
REM using aschipfl's FindReplaceWithVar, but also need Henrik's DisableDelayedExpansion
setlocal DisableDelayedExpansion
set "varStrFind=!This is a comment"
set "varStrRepl=This is no longer a comment"
set "varFile=tmpfile.cfg"
call :FindReplaceWithVar varStrFind varStrRepl varFile
endlocal
REM for some reason, the script only works when I do another setlocal endlocal, not sure why I can't reassign the variables.
setlocal DisableDelayedExpansion
set "varStrFind=!This is another comment"
set "varStrRepl=This is not another comment"
set "varFile=tmpfile.cfg"
call :FindReplaceWithVar varStrFind varStrRepl varFile
endlocal
REM now back to using delayed expansion
if "!advancedOpt!" == "1" (
setlocal EnableDelayedExpansion
set "varStrFind=This option shouldn't be enabled"
set "varStrRepl=!Now I'm not enabled."
set "varFile=tmpfile.cfg"
call :FindReplaceWithVar varStrFind varStrRepl varFile
endlocal
)
exit /b
REM I still have the original :FindReplace method, but need to also use the method below.
REM This is aschipfl's modification. Need to EnableDelayedExpansion for the method
:FindReplaceWithVar
setlocal Enable
set "tmpf=%temp%\tmp.txt"
If not exist %"temp%\_.vbs" call :MakeReplace
for /F "tokens=*" %%a in ('dir /S /B /A:-D /O:N "!%~3!"') do (
for /F "usebackq" %%b in (`Findstr /MIC:"!%~1!" "%%a"`) do (
<%%a cscript //nologo %temp%\_.vbs "!%~1!" "!%~2!">"%tmpf%"
if exist "%tmpf%" move /Y "%tmpf%" "%%~dpnxa">nul
)
)
del %temp%\_.vbs
endlocal
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
因此,正如脚本中所提到的,我需要在aschipfl的FindReplace方法中启用DelayedExpansion。
我还需要包含每个包含&#34;!&#34;的字符串。使用setlocal DisableDelayedExpansion ... endlocal。我以为我可以用&#34;继续使用DisableDelayedExpansion!这是另一个评论&#34;来自上一个块,但是varStrFind和其他变量不能正确设置或者不按顺序执行脚本。
所以,至少这是有效的,我对此很满意。