2014-01-21 39 views
3

我有一個文本文件,它使用[]對不同的子系統進行分組,然後在每個子組中包含項目標誌。下面是該文件的一個片段,這樣你可以得到它是什麼樣的理解(注意,每個小組可以有相同的項目):.bat文件搜索和替換結構化文本文件中的字符串

[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文件有效地替換/更新文本文件。我創建了一個函數來讀取文件並將其寫入新文件,現在我正在嘗試開發一種乾淨的方式來識別行項目以及它們所在的子組,以根據需要替換所需的文本。

以下是我與我的計劃說: 更新:我花了一個下午寫一些代碼,似乎工作,如下圖所示,有最清晰度更好的方法。

::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)  

有沒有更好的方法來做到這一點?任何人都可以幫助完成代碼,因爲我有很多麻煩解決這個問題。

更新:感謝下面答案中提出的兩種方法。他們很長,我從他們身上學到了很多東西。不完全確定如何實現這些功能,但我最擔心的是使用該功能會使重複讀取文件的速度顯着降低。我對這個bat文件很陌生,但我知道它非常強大,如果你知道這些命令並且很有創意。

在此先感謝您的幫助。 -Dan

+0

也許你可以考慮這篇文章:http://blogs.technet.com/b/deploymentguys/archive/2010/07/15/reading-and-modifying-ini-files-with-scripts.aspx –

+0

這可能是我會爭辯爲正確工作使用正確工具的地方。我建議使用任何公開['GetPrivateProfileString'](http://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724353%28v=vs.85%29.aspx)函數系列的任何東西,因爲這是大多數程序使用的而不是從頭開始重現功能的。值得關注的腳本語言有[AutoIt](http://www.autoitscript.com/site/autoit/)。 – daalbert

+0

dbenham的REPL.BAT解決方案在處理文件時比純批處理代碼更健壯,而且比簡單批處理代碼更快。它只讀取一次文件,因此請測試他的解決方案以仔細檢查功能。 – foxidrive

回答

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機器。腳本中嵌入了完整的文檔,包括指向描述所有可用JScript正則表達式元字符的MicroSoft頁面的鏈接。

假設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改變對齊12345

MODIFY_CONFIG yourFile.ini PressureLevelControl Alignment 12345 
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,則顯示現有值的報告。

它應該是相當明顯的,你可以很容易地進行修改,以允許添加或刪除值,而不是隻是改變。

與文件名和其他事項演奏現在在那些表現出興趣的手。

相關問題