how to search and replace case sensitive string using batch

前端 未结 3 1062
旧时难觅i
旧时难觅i 2021-01-15 15:08

I want to search and replace case sensitive string

like if I have rise Rise RISE in a text file I only want to replace string \"rise\" the code below is replace a

相关标签:
3条回答
  • 2021-01-15 15:57

    This is a subject that has interested me for a long time. My personal criteria is that the solution is a script that utilizes only native Windows commands, and that it be compatible with all Windows versions from XP onward.

    I have developed two solutions: 1) A pure batch solution that I believe is about as efficient as is possible for batch, and 2) a hybrid JScript/batch solution that is extremely powerful and also very fast.

    I have pretty much abandoned the pure batch solution in favor of the JScript/batch hybrid because the hybrid is more powerful with full regex support, and it is MUCH faster.

    1) Pure batch solution: MODFILE.BAT

    I first published this at DOSTIPS: The "ultimate" file search and replace batch utility

    The batch function can be used as a stand-alone utility, or incorporated within a larger batch script.

    Assuming the function is a stand-alone utility in a file named MODFILE.BAT that is either in your current folder, or else somewhere within your PATH, then your script becomes:

    @echo off
    setlocal enableDelayedExpansion
    
    set file="c:\Users\rawal\Desktop\a\file.txt"
    set "OldStr=rise"
    set "NewStr="
    set /p "NewStr=Enter some text: "
    
    call ModFile "%file%" OldStr NewStr
    

    Here is the ModFile function itself. Full documentation is embedded within the script. I've taken great pains to optimize the code, and eliminate limitations that plague most batch solutions. But there are a few remaining limitations that are listed within the documentation.

    @echo off
    :modFile File SearchVar [ReplaceVar] [/I]
    ::
    ::  Perform a search and replace operation on each line within File.
    ::
    ::  SearchVar = A variable containing the search string.
    ::
    ::  ReplaceVar = A variable containing the replacement string.
    ::               If ReplaceVar is missing or is not defined then the
    ::               search string is replaced with an empty string.
    ::
    ::  The /I option specifies a case insensitive search.
    ::
    ::  A backup of the original File is made with an extension of .bak
    ::  prior to making any changes.
    ::
    ::  The number of replacements made is returned as errorlevel.
    ::
    ::  If an error occurs then no changes are made and
    ::  the errorlevel is set to -1.
    ::
    ::  Limitations
    ::    - File must use Windows style line terminators <CR><LF>.
    ::    - Trailing control characters will be stripped from each line.
    ::    - The maximum input line length is 1021 characters.
    ::
    setlocal enableDelayedExpansion
    
      ::error checking
      if "%~2"=="" (
        >&2 echo ERROR: Insufficient arguments
        exit /b -1
      )
      if not exist "%~1" (
        >&2 echo ERROR: Input file "%~1" does not exist
        exit /b -1
      )
      2>nul pushd "%~1" && (
        popd
        >&2 echo ERROR: Input file "%~1" does not exist
        exit /b -1
      )
      if not defined %~2 (
        >&2 echo ERROR: searchVar %2 not defined
        exit /b -1
      )
      if /i "%~3"=="/I" (
        >&2 echo ERROR: /I option can only be specified as 4th argument
        exit /b -1
      )
      if "%~4" neq "" if /i "%~4" neq "/I" (
        >&2 echo ERROR: Invalid option %4
        exit /b -1
      )
    
      ::get search and replace strings
      set "_search=!%~2!"
      set "_replace=!%~3!"
    
      ::build list of lines that must be changed, simply exit if none
      set "replaceCnt=0"
      set changes="%temp%\modFileChanges%random%.tmp"
      <"%~1" find /n %~4 "!_search:"=""!^" >%changes% || goto :cleanup
    
      ::compute length of _search
      set "str=A!_search!"
      set searchLen=0
      for /l %%A in (12,-1,0) do (
        set /a "searchLen|=1<<%%A"
        for %%B in (!searchLen!) do if "!str:~%%B,1!"=="" set /a "searchLen&=~1<<%%A"
      )
    
      ::count number of lines + 1
      for /f %%N in ('find /v /c "" ^<"%~1"') do set /a lnCnt=%%N+1
    
      ::backup source file
      if exist "%~1.bak" del "%~1.bak"
      ren "%~1" "%~nx1.bak"
    
      ::initialize
      set "skip=2"
    
      <"%~1.bak" (
    
        %=for each line that needs changing=%
        for %%l in (!searchLen!) do for /f "usebackq delims=[]" %%L in (%changes%) do (
    
          %=read and write preceding lines that don't need changing=%
          for /l %%N in (!skip! 1 %%L) do (
            set "ln="
            set /p "ln="
            if defined ln if "!ln:~1021!" neq "" goto :lineLengthError
            echo(!ln!
          )
    
          %=read the line that needs changing=%
          set /p "ln="
          if defined ln if "!ln:~1021!" neq "" goto :lineLengthError
    
          %=compute length of line=%
          set "str=A!ln!"
          set lnLen=0
          for /l %%A in (12,-1,0) do (
            set /a "lnLen|=1<<%%A"
            for %%B in (!lnLen!) do if "!str:~%%B,1!"=="" set /a "lnLen&=~1<<%%A"
          )
    
          %=perform search and replace on line=%
          set "modLn="
          set /a "end=lnLen-searchLen, beg=0"
          for /l %%o in (0 1 !end!) do (
            if %%o geq !beg! if %~4 "!ln:~%%o,%%l!"=="!_search!" (
              set /a "len=%%o-beg"
              for /f "tokens=1,2" %%a in ("!beg! !len!") do set "modLn=!modLn!!ln:~%%a,%%b!!_replace!"
              set /a "beg=%%o+searchLen, replaceCnt+=1"
            )
          )
          for %%a in (!beg!) do set "modLn=!modLn!!ln:~%%a!"
    
          %=write the modified line=%
          echo(!modLn!
    
          %=prepare for next iteration=%
          set /a skip=%%L+2
        )
    
        %=read and write remaining lines that don't need changing=%
        for /l %%N in (!skip! 1 !lnCnt!) do (
          set "ln="
          set /p "ln="
          if defined ln if "!ln:~1021!" neq "" goto :lineLengthError
          echo(!ln!
        )
    
      ) >"%~1"
    
      :cleanup
      del %changes%
    exit /b %replaceCnt%
    
    :lineLengthError
      del %changes%
      del "%~1"
      ren "%~nx1.bak" "%~1"
      >&2 echo ERROR: Maximum input line length exceeded. Changes aborted.
    exit /b -1
    


    2) Hybrid JScript/batch solution: REPL.BAT

    I first published this at DOSTIPS: regex search and replace for batch - Easily edit files!

    I really love this utility. Most batch scripting I do as a hobby, but I use this utility regularly in my day job. It is extremely powerful and fast, yet requires very little code. It supports regular expression search and replace, but also has an /L literal option. The search is case sensitive by default.

    Assuming REPL.BAT is either in your current folder, or else somewhere within your PATH, then your code becomes:

    @echo off
    setlocal enableDelayedExpansion
    
    set "file=c:\Users\rawal\Desktop\a\file.txt"
    set "OldStr=rise"
    set "NewStr="
    set /p "NewStr=Enter some text: "
    
    type "%file%" | repl OldStr NewStr VL >"%file%.new"
    move /y "%file%.new" "%file%" >nul
    

    I use the L option to force a literal search instead of the default regex search, and the V option to read the search and replace values directly from environment variables instead of passing string literals.

    Here is the actual REPL.BAT utility. Full documentation is embedded within the script.

    @if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
    
    ::************ Documentation ***********
    :::
    :::REPL  Search  Replace  [Options  [SourceVar]]
    :::REPL  /?
    :::
    :::  Performs a global search and replace operation on each line of input from
    :::  stdin and prints the result to stdout.
    :::
    :::  Each parameter may be optionally enclosed by double quotes. The double
    :::  quotes are not considered part of the argument. The quotes are required
    :::  if the parameter contains a batch token delimiter like space, tab, comma,
    :::  semicolon. The quotes should also be used if the argument contains a
    :::  batch special character like &, |, etc. so that the special character
    :::  does not need to be escaped with ^.
    :::
    :::  If called with a single argument of /? then prints help documentation
    :::  to stdout.
    :::
    :::  Search  - By default this is a case sensitive JScript (ECMA) regular
    :::            expression expressed as a string.
    :::
    :::            JScript regex syntax documentation is available at
    :::            http://msdn.microsoft.com/en-us/library/ae5bf541(v=vs.80).aspx
    :::
    :::  Replace - By default this is the string to be used as a replacement for
    :::            each found search expression. Full support is provided for
    :::            substituion patterns available to the JScript replace method.
    :::            A $ literal can be escaped as $$. An empty replacement string
    :::            must be represented as "".
    :::
    :::            Replace substitution pattern syntax is documented at
    :::            http://msdn.microsoft.com/en-US/library/efy6s3e6(v=vs.80).aspx
    :::
    :::  Options - An optional string of characters used to alter the behavior
    :::            of REPL. The option characters are case insensitive, and may
    :::            appear in any order.
    :::
    :::            I - Makes the search case-insensitive.
    :::
    :::            L - The Search is treated as a string literal instead of a
    :::                regular expression. Also, all $ found in Replace are
    :::                treated as $ literals.
    :::
    :::            B - The Search must match the beginning of a line.
    :::                Mostly used with literal searches.
    :::
    :::            E - The Search must match the end of a line.
    :::                Mostly used with literal searches.
    :::
    :::            V - Search and Replace represent the name of environment
    :::                variables that contain the respective values. An undefined
    :::                variable is treated as an empty string.
    :::
    :::            M - Multi-line mode. The entire contents of stdin is read and
    :::                processed in one pass instead of line by line. ^ anchors
    :::                the beginning of a line and $ anchors the end of a line.
    :::
    :::            X - Enables extended substitution pattern syntax with support
    :::                for the following escape sequences:
    :::
    :::                \\     -  Backslash
    :::                \b     -  Backspace
    :::                \f     -  Formfeed
    :::                \n     -  Newline
    :::                \r     -  Carriage Return
    :::                \t     -  Horizontal Tab
    :::                \v     -  Vertical Tab
    :::                \xnn   -  Ascii (Latin 1) character expressed as 2 hex digits
    :::                \unnnn -  Unicode character expressed as 4 hex digits
    :::
    :::                Escape sequences are supported even when the L option is used.
    :::
    :::            S - The source is read from an environment variable instead of
    :::                from stdin. The name of the source environment variable is
    :::                specified in the next argument after the option string.
    :::
    
    ::************ Batch portion ***********
    @echo off
    if .%2 equ . (
      if "%~1" equ "/?" (
        findstr "^:::" "%~f0" | cscript //E:JScript //nologo "%~f0" "^:::" ""
        exit /b 0
      ) else (
        call :err "Insufficient arguments"
        exit /b 1
      )
    )
    echo(%~3|findstr /i "[^SMILEBVX]" >nul && (
      call :err "Invalid option(s)"
      exit /b 1
    )
    cscript //E:JScript //nologo "%~f0" %*
    exit /b 0
    
    :err
    >&2 echo ERROR: %~1. Use REPL /? to get help.
    exit /b
    
    ************* JScript portion **********/
    var env=WScript.CreateObject("WScript.Shell").Environment("Process");
    var args=WScript.Arguments;
    var search=args.Item(0);
    var replace=args.Item(1);
    var options="g";
    if (args.length>2) {
      options+=args.Item(2).toLowerCase();
    }
    var multi=(options.indexOf("m")>=0);
    var srcVar=(options.indexOf("s")>=0);
    if (srcVar) {
      options=options.replace(/s/g,"");
    }
    if (options.indexOf("v")>=0) {
      options=options.replace(/v/g,"");
      search=env(search);
      replace=env(replace);
    }
    if (options.indexOf("l")>=0) {
      options=options.replace(/l/g,"");
      search=search.replace(/([.^$*+?()[{\\|])/g,"\\$1");
      replace=replace.replace(/\$/g,"$$$$");
    }
    if (options.indexOf("b")>=0) {
      options=options.replace(/b/g,"");
      search="^"+search
    }
    if (options.indexOf("e")>=0) {
      options=options.replace(/e/g,"");
      search=search+"$"
    }
    if (options.indexOf("x")>=0) {
      options=options.replace(/x/g,"");
      replace=replace.replace(/\\\\/g,"\\B");
      replace=replace.replace(/\\b/g,"\b");
      replace=replace.replace(/\\f/g,"\f");
      replace=replace.replace(/\\n/g,"\n");
      replace=replace.replace(/\\r/g,"\r");
      replace=replace.replace(/\\t/g,"\t");
      replace=replace.replace(/\\v/g,"\v");
      replace=replace.replace(/\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}/g,
        function($0,$1,$2){
          return String.fromCharCode(parseInt("0x"+$0.substring(2)));
        }
      );
      replace=replace.replace(/\\B/g,"\\");
    }
    var search=new RegExp(search,options);
    
    if (srcVar) {
      WScript.Stdout.Write(env(args.Item(3)).replace(search,replace));
    } else {
      while (!WScript.StdIn.AtEndOfStream) {
        if (multi) {
          WScript.Stdout.Write(WScript.StdIn.ReadAll().replace(search,replace));
        } else {
          WScript.Stdout.WriteLine(WScript.StdIn.ReadLine().replace(search,replace));
        }
      }
    }
    
    0 讨论(0)
  • 2021-01-15 16:02
    @ECHO OFF
    SETLOCAL
    SET "old=rise"
    SET "new=deflate"
    DEL newfile.txt /F /Q
    FOR /f "delims=" %%i IN ('type somefile.txt^|findstr /n "$" ') DO (
    ECHO %%i
    SET line=%%i
    CALL :replace
    )
    
    FC somefile.txt newfile.txt
    
    GOTO :eof
    
    :REPLACE
    :: first replace all characters up to the colon by nothing
    SET line=%line:*:=%
    SET "withreplacements="
    :loop
    IF NOT DEFINED line >>newfile.txt ECHO(%withreplacements%&GOTO :EOF 
    ECHO %line%|FINDSTR /b /l /c:"%old%" >NUL
    IF ERRORLEVEL 1 SET withreplacements=%withreplacements%%line:~0,1%&SET line=%line:~1%&GOTO loop
    SET withreplacements=%withreplacements%%new%
    SET remove=%old%
    :loploop
    IF DEFINED remove SET remove=%remove:~1%&SET line=%line:~1%&GOTO loploop
    GOTO loop
    

    Here's a relatively simple method. It has a marked sensitivity to certain characters, "^&|<> being the problems - perhaps some others too - but space,;%!)( seem fine.

    It reads every line by numbering usinf FINDSTR which places linenumber : at the beginning of each line

    That prefix is removed and the withreplacements line built character-by-character

    • see whether the line starts with the target replaceme string
    • If it doesn't remove the first character, place it onto the end of the string being built
    • if it does match,
      • append the replacement string
      • make a copy of the string-to-replace
      • remove the first character of the source string and the copy-to-replace until the copy-to-replace becomes empty

    and repeat until the original line becomes empty

    Yes - it's S-L-O-W. But it works. Kinda.

    Improvement suggestions welcome.

    0 讨论(0)
  • 2021-01-15 16:15

    We all know that Batch files have multiple restrictions, so the creation of general purpose solutions is difficult. Because of this, I always try to fullfill the particular requirements of a certain given problem first. If this is possible, then the limitations of Batch to provide a more general solution for other similar problems that are NOT currently being requested by someone don't matters, right?

    The Batch file below do a case-sensitive replacement of one string by another one and it is very fast, but it fail in lines that contain the original string written MORE THAN ONCE in different case combinations, including the target one. I think this method is enough for most users that have this requirement.

    @echo off
    setlocal EnableDelayedExpansion
    
    set /P "file=Enter file name: "
    set /P "OldStr=Enter original text: "
    set /P "NewStr=Enter new text: "
    
    rem Get list of numbers of matching lines to replace
    set n=0
    for /F "delims=:" %%a in ('findstr /N /C:"%OldStr%" "%file%"') do (
       set /A n+=1
       set replace[!n!]=%%a
    )
    if %n% equ 0 (
       echo Original text not found in file
       goto :EOF
    )
    set /A n+=1
    set replace[%n%]=0
    
    rem Process all lines in the file
    setlocal DisableDelayedExpansion
    set i=1
    (for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "%file%"') do (
       set line=
       set "line=%%b"
       setlocal EnableDelayedExpansion
       rem If this line have the original string...
       for %%i in (!i!) do if %%a equ !replace[%%i]! (
          rem ... replace it and advance to next matching line number
          echo !line:%OldStr%=%NewStr%!
          endlocal & set /A i=%%i+1
       ) else (
          echo(!line!
          endlocal
       )
    )) > "%file%_new.txt
    rem If you want to replace the original file, remove REM from next line:
    REM move /Y "%file%_new.txt" "%file%"
    

    For example, this input file:

    This line is not changed: Rise. 
    No problem with special characters: & | < > ! " ^ 
    This line is changed: rise
    This line is not changed: RISE
    This line is incorrectly changed: Rise & rise
    

    with a replacement of "rise" by "New Text", produce:

    This line is not changed: Rise. 
    No problem with special characters: & | < > ! " ^ 
    This line is changed: New Text
    This line is not changed: RISE
    This line is incorrectly changed: New Text & New Text
    
    0 讨论(0)
提交回复
热议问题