Windows批处理:带有感叹号的字符串,用作未正确解析的函数的参数

时间:2016-01-07 19:49:24

标签: batch-file

我有一个文本文件,我想将注释的字符串更改为其他值,反之亦然。文本文件中的注释字符串以感叹号(!)开头。我正在使用以下文章中提到的F​​indReplace函数: 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
    )
 )

4 个答案:

答案 0 :(得分:3)

奇怪的行为的原因是({1}},%1%2等(子程序)参数在很早的状态下被解析,早在延迟变量扩展之前很久完成(实际上即使在立即扩展之前)。

要克服这个问题,您需要避免将字符串/值作为参数传递给子例程。我看到以下选项:

  1. 全局变量

    在调用子例程之前,需要将子程序要处理的字符串/值分配给全局变量。这些在延迟扩展的子程序中读取:

    %3
  2. 传递变量名称

    在调用子例程之前,需要将子程序要处理的字符串/值分配给变量。然后将变量的名称作为参数传递给子例程。使用延迟扩展间接读取子程序中的变量:

    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...
    
  3. 注意:
    要了解命令提示符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)

好的,我终于明白了。首先,我要感谢Henrik,Magoo和aschipfl的建议。我不得不对这些建议进行混合,这就是我提供这个答案的原因。

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和其他变量不能正确设置或者不按顺序执行脚本。

所以,至少这是有效的,我对此很满意。