.bat文件搜索并替换结构化文本文件中的字符串

时间:2014-01-21 15:59:30

标签: string batch-file replace

我有一个文本文件,使用[]对不同的子系统进行分组,然后在每个子组中包含项目标志。这是一个文件片段,您可以了解它的外观(注意每个子组可以有相同的项目):

   [EV]
   Verbosity=0
   Alignment=123

   [FluidLevelControl]
   BufferTypeLastUsed=TWEEN
   Enable Dip Tube=no
   Alignment=456,efg

   [PressureLevelControl]
   Enabled=yes
   Alignment=789,abc
   Calibration Date=1280919634

   [BufferFrontValve]
   Log=yes
   Alignment=987

注意,上面的文件超过了2000行。我想这个脚本需要一段时间才能执行。我也知道有一个更好的框架可以做到这一点,但在我们的应用程序中,我们需要它从闪存驱动器运行,并能够插入我们的仪器运行WinXP没有.NET框架等。

我想要做的是使用.bat文件在文档中搜索特定子系统(即[DesiredSubsystem])和子系统中的所需项目,然后修改项目数据。例如,在上面的文本中,我可能想要在PressureLevelControl子组中将对齐从789更改为12345。

我知道无法使用bat文件有效替换/更新文本文件。我已经创建了一个读取文件并将其写入新文件的函数,现在我正在尝试开发一种干净的方法来识别行项目以及它们所在的子组,以便根据需要替换所需的文本。 / p>

以下是我对我的计划所评论的内容: 更新:我花了一个下午编写了一些似乎如下所示工作的代码,有大多数def更好的方法。

    ::SET VARS
        set "varDebugFP=\\svchafile\Teams\Test Engineering\Productivity Tools\MFG BAT Files\SpecificTest\"
        set varSource=%varDebugFP%Debug\
        set varDestination=%varDebugFP%Debug\
        set varFileName=specific.ini

    ::Do Text File Editing
        setlocal enableDelayedExpansion
        set "LastGroup=NONE"
        ::preserve blank lines using FINDSTR, even if a line may start with :
        for /f "usebackq delims=*" %%A in (`type "%srcFile%" ^| findstr /n "^"`) do         (
            set "strLine=%%A"
            set "strLine=!strLine:*:=!"

            ::Check to see if the is defined and greater than 2 characters inidicating a good line
            if defined strLine if NOT "!strLine:~2,1!"=="" if         "!strLine:~0,1!!strLine:~-1!"=="[]" (set "LastGroup=!strLine!")

            ::Set the paramaters looking to match
            set "DesiredGroup=[TestGroup]"
            set "DesiredItem=TestItem"
            set "ReplaceLineWith=NewTestItemLine=NewData"
            ::Look for match on current line
            if defined strLine if "!LastGroup!"=="!DesiredGroup!" if NOT "!strLine!"=="!strLine:TestItem=Mod!" (set "strLine=!ReplaceLineWith!")
            ::Note, in the above line I would like 'TestItem' to be the 'DesiredItem' variable but I can't get it working due to the DelayedExpansion

            ::Set the additonal paramaters looking to match
            ::Note, there are multiple items I want to change at once without having to reitterate through the org long (2000+lines) file
            set "DesiredGroup=[TestGroup2]"
            set "DesiredItem=TestItem2"
            set "ReplaceLineWith=NewTestItemLine2=NewData2"
            if defined strLine if "!LastGroup!"=="!DesiredGroup!" if NOT "!strLine!"=="!strLine:TestItem=Mod!" (set "strLine=!ReplaceLineWith!")

            ::I plan to copy and paste the above section as many times as needed to capture all the lines I need to edit (at this point about ~10)

            ::I don't really understand why the "(" in the below line, I found it in an example on stackoverflow and it seems to work.
            echo(!strLine!>>"%newFile%"
        )
        endlocal


    ::Replace org file with new file, delete org file (this part I have figured out)    

有更好的方法吗?任何人都可以帮助完成代码,因为我在解析这个问题时遇到了很多麻烦。

更新:感谢您在下面的答案中提出的两种方法。它们很长,我从中学到了很多东西。但并不完全确定如何实现这些功能,我最担心的是使用该功能会使文件中的重复读取速度大大降低。我对这个蝙蝠文件很新,但我知道它非常强大,如果你知道命令并且很有创意。

提前感谢您提供任何帮助。 -Dan

2 个答案:

答案 0 :(得分:4)

有很多人想使用批处理来编辑文本文件 - 有很多 很多 SO问题来处理这个主题。但是,仅使用本机批处理命令来执行此操作非常困难(而且相对较慢)。

最好使用其他工具。一个不错的选择是使用像sed或awk的免费Windows端口。但那些需要将非本机可执行文件下载到您的计算机上,这在许多办公室都是禁止的。

我写过REPL.BAT - a hybrid JScript/batch utility that performs a regular expression search and replace on stdin and writes the result to stdout.。该脚本仅使用从XP开始的所有现代Windows机器可用的本机脚本。完整文档嵌入在脚本中,包括指向MicroSoft页面的链接,该页面描述了所有可用的JScript正则表达式元字符。

假设REPL.BAT位于当前目录中,或者更好,位于PATH中的某个位置,则可以使用以下简单批处理脚本来修改特定子系统中任何项目的值。

::MODIFY_CONFIG.BAT  File  SubSystem  Item  NewValue
::
::  Any argument that contains spaces or special characters should be quoted.
::
::  File      = File to modify, may include full path
::  SubSystem = The section containing the item to modify (without brackets)
::  Item      = The Item within the SubSystem that is to be modified
::  NewValue  = The new value for the item

@echo off
type "%~1"|repl "(^ *\[%~2] *\r?\n(?: *[^[].*\n)*? *%~3=)[^\r\n]*" "$1%~4" m >"%~1.new"
move /y "%~1.new" "%~1" >nul

这是对脚本的调用,它将PressureLevelControl中的Alignment更改为12345

MODIFY_CONFIG yourFile.ini PressureLevelControl Alignment 12345

答案 1 :(得分:2)

@ECHO OFF
SETLOCAL
:: Read parameters
:: %1 is subgroup
:: %2 is item
:: %3 is new value
:: %3 missing = report value
SET "subgroup=%~1"
SET "item=%~2"
SET "newval=%~3"
IF NOT DEFINED subgroup ECHO syntax:%~nx0 "subgroup" "item" "newvalue"&GOTO :EOF 
IF NOT DEFINED item     ECHO syntax:%~nx0 "subgroup" "item" "newvalue"&GOTO :EOF 
ECHO %*
:: state=0 (looking for subgroup) 1 (found subgroup)
SET /a state=0
:: result=0 (did nothing) 2 (found subgroup, not data line) 3 (found subgroup more than once)
:: 4 (found and replaced data line) 5  (found subgroup more than once, replaced data once)
:: 6 (detected dataline more than once, replaced once) 9 (reporting only - value found)
SET /a result=0
(
 FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r "^" q21263073.txt') DO (
  SET "line=%%b"
  CALL :process
  IF DEFINED repro ECHO(%%b
  REM pause
 )
)>newfile.txt

SET "replacefile="
CALL :report%result%
IF DEFINED replacefile ECHO A NEW FILE HAS BEEN CREATED

GOTO :EOF

:report0
ECHO [%subgroup%] NOT found
GOTO :eof

:report2
ECHO [%subgroup%] %item% NOT found
GOTO :eof

:report3
ECHO [%subgroup%] found repeatedly - %item% NOT found
GOTO :eof

:report4
ECHO [%subfound%] %olditem% found replaced %oldvalue% with %newval%
SET replacefile=Y
GOTO :eof

:report5
ECHO [%subgroup%] found repeatedly - %olditem% found replaced %oldvalue% with %newval%
GOTO :eof

:report6
ECHO [%subgroup%] %item% found repeatedly - %olditem% found replaced %oldvalue% with %newval% ONCE
GOTO :eof

:report9
ECHO [%subgroup%] %olditem% found with value %oldvalue%
GOTO :eof

:process
:: blank line ?
SET repro=Y
IF NOT DEFINED line GOTO :EOF
IF "%line:~0,1%%line:~-1%"=="[]" GOTO fsubsys
:: only process data lines if state=1
IF NOT %state%==1 GOTO :EOF
IF %result% gtr 5 GOTO :EOF
SET "fvalue="
FOR /f "tokens=1*delims==" %%p IN ("%line%") DO SET "fitem=%%p"&SET "fvalue=%%q"
:: Did we have an item=value line?
IF NOT DEFINED fvalue GOTO :EOF
CALL :matchit "%fitem%" "%item%"
IF NOT DEFINED matched GOTO :eof
:: we found a matching item within a subgroup.
:: result must be 2,3,4 or 5
FOR %%z IN (2.4 3.5 4.6 5.6) DO FOR /f "tokens=1,2delims=." %%c IN ("%%z") DO IF %result%==%%c SET result=%%d
IF %result%==6 GOTO :EOF
:: Haven't yet replaced value
:: Do we have a replacement?
SET "olditem=%fitem%"&SET "oldvalue=%fvalue%"
IF NOT DEFINED newval SET result=9&GOTO :eof
SET "repro="
ECHO(%fitem%=%newval%

GOTO :eof

:: found a subgroup name
:fsubsys
SET /a state=0
:: Is it the one we're looking for?
CALL :matchit "%line:~1,-1%" "%subgroup%"
IF NOT DEFINED matched GOTO :eof
SET /a state=1
FOR %%z IN (0.2 2.3 4.5) DO FOR /f "tokens=1,2delims=." %%c IN ("%%z") DO IF %result%==%%c SET result=%%d
IF %result%==2 SET "subfound=%line:~1,-1%"
GOTO :eof

:: match %1 to %2. If matches, set matched to not empty. if not, set matched to empty
:: here is where we can have some fun.
:matchit
SET "matched="
SET "string1=%~1"
SET "string2=%~2"
:: Case-insensitive exact match?
IF /i "%string1%"=="%string2%" SET matched=Y&GOTO :EOF
:: partial-string match. If specified item begins "+" then match rest against item found
:: so +ali matches "Alignment"
IF NOT %string2:~0,1%==+ GOTO npsm
CALL SET string3=%%string1:*%string2:~1%=%%
IF /i "%string3%"=="%string1%" GOTO :eof
IF /i "%string2:~1%%string3%"=="%string1%" SET matched=Y
GOTO :EOF
:: initials - so "Enable Dip Tube" is matched by "edt"
:npsm
CALL :inits %string1%
IF /i "%string3%"=="%string2%" SET matched=Y
GOTO :eof

:inits
SET "string3=%2"
IF NOT DEFINED string3 GOTO :EOF
SET "string3="
:initsl
SET string1=%1
IF NOT DEFINED string1 GOTO :EOF
SET string3=%string3%%string1:~0,1%
SHIFT
GOTO initsl

好的 - 我被带走......

我使用名为q21263073.txt的文件和我的测试样本数据。如果需要,将生成文件newfile.txt,并且可以替换原始文件(仅在定义replacefile时才建议。)

必需的语法是 thisbatch 子组项newvalue

任何包含空格的参数都应为"quoted"

特点:

  1. 名称匹配不区分大小写。
  2. 您可以使用前导 + 缩写为唯一的字符串开头,因此+pr 会匹配PressureLevelControl
  3. 如果缩写在某个部分中不唯一,则会生成错误报告。
  4. 您可以将Space Separated Names缩写为首字母SSN
  5. 如果省略newvalue,则会显示现有值的报告。
  6. 很明显,您可以轻松地进行修改,以允许添加或删除值,而不是仅仅更改。

    使用文件名和其他事项现在掌握在那些有兴趣的人手中。