简体   繁体   中英

Windows command line string replace removes “!”

I have this little snippet that replaces production:false with production:true .

(FOR /F "tokens=1,* delims=]" %%A in ('"type test.js|find /n /v """') do (

    set "line=%%B"

    if defined line (
        call set "line=echo.%%line:production:false=production:true%%"
        FOR /F "delims=" %%X in ('"echo."%%line%%""') do %%~X
    ) ELSE echo.

)) >test-temp.js

move /Y test-temp.js test.js

So far so good, but, in the test.js it says something like:

if ( !production ) {}

The thing is, the "!" is removed by the above command as well. Any idea how does happens?

You probably have delayedexpansion invoked.

Running your snippet (which is all you give us) against your single line of provided data simply reproduced the data line verbatim.

with delayedexpansion , the ! disappeared as you describe.


To fix (with delayedexpansion in effect

SETLOCAL DISABLEDELAYEDEXPANSION
(FOR /F "tokens=1,* delims=]" %%A in ('"type q19406461.txt|find /n /v """') do (
    set "line=%%B"

    if defined line (
             call set "line=echo.%%line:production:false=production:true%%"
             FOR /F "delims=" %%X in ('"echo."%%line%%""') do %%~X
    ) ELSE echo.

)) >test-temp.js
ENDLOCAL

This is a robust method and uses a helper batch file called repl.bat from - http://www.dostips.com/forum/viewtopic.php?f=3&t=3855

Put repl.bat in the same folder as the batch file.

@echo off
type "test.js"|repl "production:false" "production:true" >"test-temp.js"

Peter already diagnosed the problem with delayed expansion. Here is a bit more explanation.

FOR variables are expanded before delayed expansion. So the line set "line=%%B" first sets the value to if ( !production ) {} , but then delayed expansion sees the unpaired ! and strips it. If it were paired, it would attempt to expand the variable in between.

Here is a summary of the order of variable expansion (practical, but a bit imprecise):

1) Normal expansion: argument (%1) and variable (%var%). Arguments take precedence over variables.

2) FOR variable: %%A

3) Delayed expansion: !var!

4) SET /A variables

See How does the Windows Command Interpreter (CMD.EXE) parse scripts for a more exact description of how lines are parsed and expanded.

It is possible to use delayed expansion within your loop without corrupting ! literals if you toggle delayed expansion on and off. This is preferred over using the CALL syntax because it is faster, does not muck with % literals, does not double quoted carets, protects against all poison characters, even when they are not quoted.

The use of delayed expansion makes the code much simpler, faster, and more reliable:

setlocal disableDelayedExpansion
(FOR /F "tokens=1,* delims=]" %%A in ('"type test.js|find /n /v """') do (
  set "line=%%B"
  setlocal enableDelayedExpansion
  if defined line (
    echo(!line:production:false=production:true!
  ) ELSE echo(
  endlocal
)) >test-temp.js
move /Y test-temp.js test.js
endlocal

Note that I use ECHO( instead of ECHO. because the latter can fail in some obscure scenarios. ECHO( looks like it would cause problems with block parentheses, but it actually always works without any problems.

Note that I would still not use the above code. Instead I would use the REPL.BAT utility as foxidrive has in his answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM