how to search and replace case sensitive string using batch

一笑奈何 提交于 2019-12-07 21:57:55

问题


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 all three strings.

Kindly help me !

@Echo on
SETLOCAL ENABLEEXTENSIONS
SETLOCAL DISABLEDELAYEDEXPANSION

set file="c:\Users\rawal\Desktop\a\file.txt"
set /p Input=Enter some text:
set OldStr="rise"
set NewStr=%Input% 

for /f "tokens=1,* delims=]" %%A in ('"type %file% |find /n /v """') do (
set "line=%%B"
if defined line (
call echo %%line:%OldStr%=%NewStr%%%>> %file%_new
) ELSE echo.
)

move /Y %file%_new %file% > nul

回答1:


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));
    }
  }
}



回答2:


@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.




回答3:


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


来源:https://stackoverflow.com/questions/15986001/how-to-search-and-replace-case-sensitive-string-using-batch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!