使用 for /f 与 if 语句和变量查找和替换字符串
Find & Replace string using for /f with if statement and variables
我写了一个批处理文件,我想用另一个 .txt 文件中的字符串覆盖关键字符串。
目前它完美地复制了新的 File.txt 文件,但不会用旧 File.txt 文件中的字符串替换字符串。
File.txt 文件中的字符串示例:
...
# Password
Pword=
# AccountName
Account=
# TownName
Town=
# Postcode
Postcode=
# LocationChangedDate
LocationChanged=
我要替换的旧File.txt 文件中的字符串示例:
...
# Password
Pword=ABC
# AccountName
Account=123
# TownName
Town=LDN
# Postcode
Postcode=WS77TP
# LocationChangedDate
LocationChanged=01/01/2015
谁能给我指出正确的方向或解释我哪里出错了?
@echo off
setlocal disableDelayedExpansion
::Variables
set InputFile=F:\EXCHANGE\Machine\File.txt
set OutputFile=F:\EXCHANGE\File-New.txt
set CopyFile=F:\EXCHANGE\OldMachine\OldFile.txt
set _strFindPword=Pword=.*
for /F "delims=" %%A in ('findstr /x "Pword=.*" %CopyFile%') do set _strInsertPword=%%A
echo.%_strInsertPword%
set _strFindAccount=Account=.*
for /F "delims=" %%B in ('findstr /x "Account=.*" %CopyFile%') do set _strInsertAccount=%%B
echo.%_strInsertAccount%
set _strFindTown=Town=.*
for /F "delims=" %%C in ('findstr /x "Town=.*" %CopyFile%') do set _strInsertTown=%%C
echo.%_strInsertTown%
set _strFindLocationChanged=LocationChanged=.*
for /F "delims=" %%D in ('findstr /x "LocationChanged=.*" %CopyFile%') do set _strInsertLocationChanged=%%D
echo.%_strInsertLocationChanged%
set _strFindPostcode=Postcode=.*
for /F "delims=" %%E in ('findstr /x "Postcode=.*" %CopyFile%') do set _strInsertPostcode=%%E
echo.%_strInsertPostcode%
(
for /F "delims=" %%L in ('findstr /n "^" "%InputFile%"') do (
set "line=%%L"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if "%%L" equ "_strFindPword" (echo.!_strInsertPword!) else (
if "%%L" equ "%_strFindAccount%" (echo.!_strInsertAccount!) else (
if "%%L" equ "%_strFindTown%" (echo.!_strInsertTown!) else (
if "%%L" equ "%_strFindLocationChanged%" (echo.!_strInsertLocationChanged!) else (
if "%%L" equ "%_strFindPostcode%" (echo.!_strInsertPostcode!) else (echo.!line!)
)
)
)
)
endlocal
)
) > "%OutputFile%"
del %InputFile%
ren %OutputFile% File.txt
pause
也许是这样的
set CopyFile=oldfile.txt
set InputFile=newfile.txt
set str_search="Pword"
for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
set str_replace="%str_replace%"
echo %str_search%
echo %str_replace%
pause
CALL :far %InputFile% %str_search% %str_replace%
EXIT /B 0
:far
setlocal enableextensions disabledelayedexpansion
set "search=%2"
set "replace=%3"
::remove quotes
set search=%search:"=%
set replace=%replace:"=%
echo %search%
echo %replace%
set "textFile=%1"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
EXIT /B 0
在 for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
处,您将包含所需信息的变量的行写入 str_replace。
之后你的程序调用了一个嵌入的查找和替换函数(:far
),我从 Batch script to find and replace a string in text file without creating an extra output file for storing the modified file 偷走了它
此函数查找字符串 "Pword" 并将其替换为旧文件中的行 find.
注意:
这并不能完全解决您的问题,因为您的新文件必须像这样 s.th。
#Password
Pword
所以如果你松开 =
它会起作用,否则它不会。我希望这可以帮助您解决问题。
我想我终于明白了...
它的作用:
- 它遍历 OldFile.txt 内容,搜索标记,如果找到它们将存储到环境变量中以在嵌套步骤中使用(例如
_PWD
标记(变量),其值为 Pword=
,它将创建一个内容为 Pword=ABC
) 的 _PWDCONTENTS
变量。
- 遍历File.txt内容,寻找相同的标记,如果找到一个标记,则将相应的
CONTENTS
变量转储到OutFile.txt,否则原行。因为这发生在内部 for
循环中,所以我不得不添加一些额外的逻辑(_WROTE
变量)以避免多次写入相同的行。
备注:
应该(嗯,除了做它应该做的)是"configurable"(代码很复杂,它正在走向meta :) 如果你愿意的话),意思是如果标记之间有变化,代码不应该改变(当然会有代码变化,但不是仅在变量定义中的功能部分)。让我详细说说:
- 如果您不再需要替换
Town=
字符串,那么您只需从 _ALL
中删除 _TOWN
:set _ALL=_PWD _ACCT _POST _LOC
.
- 相反:如果你想添加一些其他标签(我们称之为Name),你必须创建一个新的环境变量:
set _NAME=Name=
并添加它到 _ALL
: set _ALL=_PWD _ACCT _TOWN _POST _LOC _NAME
.
作为间接结果,我没有关注性能,所以它可能 运行 慢。无论如何,我试图将磁盘访问(非常慢)保持在最低限度(一个例子是当有 2 for
循环迭代文件内容时 - 假设每次迭代都需要磁盘访问;这可能不是真的,Win 有 IO 缓冲 - 它是外部缓冲。
- 我"commented"把文件的最后一行去掉,避免覆盖原文件。如果需要该行为,只需删除开头的
rem
。
这是批处理代码:
@echo off
setlocal enabledelayedexpansion
set _INFILE="File.txt"
set _OUTFILE="NewFile.txt"
set _OLDFILE="OldFile.txt"
set _PWD=Pword=
set _ACCT=Account=
set _TOWN=Town=
set _POST=Postcode=
set _LOC=LocationChanged=
set _ALL=_PWD _ACCT _TOWN _POST _LOC
echo Parsing old file contents...
for /f "tokens=*" %%f in ('type !_OLDFILE!') do (
for %%g in (!_ALL!) do (
echo %%f | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
set %%gCONTENTS=%%f
)
)
)
copy nul %_OUTFILE%
echo Merging the old file contents into the new file...
set _WROTE=0
for /f "tokens=*" %%f in ('findstr /n "^^" !_INFILE!') do (
set _TMPVAR0=%%f
set _TMPVAR0=!_TMPVAR0:*:=!
for %%g in (!_ALL!) do (
echo !_TMPVAR0! | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
echo.!%%gCONTENTS!>>!_OUTFILE!
set _WROTE=1
)
)
if "!_WROTE!" equ "0" (
echo.!_TMPVAR0!>>!_OUTFILE!
) else (
set _WROTE=0
)
)
rem copy /-y %_OUTFILE% %_INFILE%
@EDIT0:使用@StevoStephenson 建议(作为问题片段的一部分),我将(第二个)外部 for
循环替换为 ('findstr /n "^^" !_INFILE!')
为了包括空行,所以第三条评论不再适用(删除)。还做了一些小的更改以允许在其路径中包含 SPACE 的文件。
它并不完美,但可能适合您:
@Echo Off
Setlocal EnableExtensions DisableDelayedExpansion
(Set InputFile=F:\EXCHANGE\Machine\File.txt)
(Set OutputFile=F:\EXCHANGE\File-New.txt)
(Set CopyFile=F:\EXCHANGE\OldMachine\OldFile.txt)
For /F "Delims=" %%I In (
'FindStr/B "Pword= Account= Town= LocationChanged= Postcode=" "%CopyFile%"'
) Do Set %%I
(For /F "Tokens=1-2* Delims=]=" %%I In ('Find /V /N ""^<"%InputFile%"') Do (
Echo(%%J|FindStr/B # || (If Defined %%J (Call Echo=%%J=%%%%J%%) Else (
If "%%J" NEq "" (Echo=%%J=%%K) Else (Echo=)))))>%OutputFile%
Timeout -1
EndLocal
Exit/B
我把删除和重命名留给你在最后添加。
这个解决方案应该比其他解决方案快得多。
它还将保留空行和包含 !
和 ^
.
的行
它只需要一个 findstr
调用来收集所有单词的旧值。
第二个 findstr
确定 infile
中需要更新的所有行(按行号)。
@echo off
setlocal EnableDelayedExpansion
set "_INFILE=File.txt"
set "_OUTFILE=NewFile.txt"
set "_OLDFILE="OldFile.txt"
set "_WORDS=Pword= Account= Town= Postcode= LocationChanged="
REM *** get all values for the key words
for /F "tokens=1,* delims==" %%L in ('findstr "!_WORDS!" "!_OLDFILE!"') do (
for /F %%S in ("%%L") do (
set "word[%%S]=%%M"
)
)
REM *** Find all lines which needs an update
set wordIdx=0
for /F "tokens=1,2,* delims=:= " %%1 in ('findstr /n "!_WORDS!" "!_INFILE!"') do (
set "lines[!wordIdx!].line=%%1"
set "lines[!wordIdx!].word=%%2"
set "replace=!word[%%2]!"
set "lines[!wordIdx!].replace=!replace!"
set /a wordIdx+=1
)
REM *** copy the infile to the outfile
REM *** Replace only the lines which are marked by line numbers
echo Parsing old file contents...
set nextWordIdx=0
set /a searchLine=lines[!nextWordIdx!].line
set lineNo=0
setlocal DisableDelayedExpansion
(
for /f "tokens=*" %%L in ('findstr /n "^" "%_INFILE%"') do (
set "line=%%L"
set /a lineNo+=1
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if !lineNo! equ !searchLine! (
(echo(!line!!lines[0].replace!)
set /a nextWordIdx+=1
for /F %%R in ("!nextWordIdx!") do (
endlocal
set /a nextWordIdx=%%R
set /a searchLine=lines[%%R].line
)
) ELSE (
(echo(!line!)
endlocal
)
)
) > "!_OUTFILE!"
我写了一个批处理文件,我想用另一个 .txt 文件中的字符串覆盖关键字符串。
目前它完美地复制了新的 File.txt 文件,但不会用旧 File.txt 文件中的字符串替换字符串。
File.txt 文件中的字符串示例:
...
# Password
Pword=# AccountName
Account=# TownName
Town=# Postcode
Postcode=# LocationChangedDate
LocationChanged=
我要替换的旧File.txt 文件中的字符串示例:
...
# Password
Pword=ABC# AccountName
Account=123# TownName
Town=LDN# Postcode
Postcode=WS77TP# LocationChangedDate
LocationChanged=01/01/2015
谁能给我指出正确的方向或解释我哪里出错了?
@echo off
setlocal disableDelayedExpansion
::Variables
set InputFile=F:\EXCHANGE\Machine\File.txt
set OutputFile=F:\EXCHANGE\File-New.txt
set CopyFile=F:\EXCHANGE\OldMachine\OldFile.txt
set _strFindPword=Pword=.*
for /F "delims=" %%A in ('findstr /x "Pword=.*" %CopyFile%') do set _strInsertPword=%%A
echo.%_strInsertPword%
set _strFindAccount=Account=.*
for /F "delims=" %%B in ('findstr /x "Account=.*" %CopyFile%') do set _strInsertAccount=%%B
echo.%_strInsertAccount%
set _strFindTown=Town=.*
for /F "delims=" %%C in ('findstr /x "Town=.*" %CopyFile%') do set _strInsertTown=%%C
echo.%_strInsertTown%
set _strFindLocationChanged=LocationChanged=.*
for /F "delims=" %%D in ('findstr /x "LocationChanged=.*" %CopyFile%') do set _strInsertLocationChanged=%%D
echo.%_strInsertLocationChanged%
set _strFindPostcode=Postcode=.*
for /F "delims=" %%E in ('findstr /x "Postcode=.*" %CopyFile%') do set _strInsertPostcode=%%E
echo.%_strInsertPostcode%
(
for /F "delims=" %%L in ('findstr /n "^" "%InputFile%"') do (
set "line=%%L"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if "%%L" equ "_strFindPword" (echo.!_strInsertPword!) else (
if "%%L" equ "%_strFindAccount%" (echo.!_strInsertAccount!) else (
if "%%L" equ "%_strFindTown%" (echo.!_strInsertTown!) else (
if "%%L" equ "%_strFindLocationChanged%" (echo.!_strInsertLocationChanged!) else (
if "%%L" equ "%_strFindPostcode%" (echo.!_strInsertPostcode!) else (echo.!line!)
)
)
)
)
endlocal
)
) > "%OutputFile%"
del %InputFile%
ren %OutputFile% File.txt
pause
也许是这样的
set CopyFile=oldfile.txt
set InputFile=newfile.txt
set str_search="Pword"
for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
set str_replace="%str_replace%"
echo %str_search%
echo %str_replace%
pause
CALL :far %InputFile% %str_search% %str_replace%
EXIT /B 0
:far
setlocal enableextensions disabledelayedexpansion
set "search=%2"
set "replace=%3"
::remove quotes
set search=%search:"=%
set replace=%replace:"=%
echo %search%
echo %replace%
set "textFile=%1"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
EXIT /B 0
在 for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
处,您将包含所需信息的变量的行写入 str_replace。
之后你的程序调用了一个嵌入的查找和替换函数(:far
),我从 Batch script to find and replace a string in text file without creating an extra output file for storing the modified file 偷走了它
此函数查找字符串 "Pword" 并将其替换为旧文件中的行 find.
注意: 这并不能完全解决您的问题,因为您的新文件必须像这样 s.th。
#Password
Pword
所以如果你松开 =
它会起作用,否则它不会。我希望这可以帮助您解决问题。
我想我终于明白了...
它的作用:
- 它遍历 OldFile.txt 内容,搜索标记,如果找到它们将存储到环境变量中以在嵌套步骤中使用(例如
_PWD
标记(变量),其值为Pword=
,它将创建一个内容为Pword=ABC
) 的_PWDCONTENTS
变量。 - 遍历File.txt内容,寻找相同的标记,如果找到一个标记,则将相应的
CONTENTS
变量转储到OutFile.txt,否则原行。因为这发生在内部for
循环中,所以我不得不添加一些额外的逻辑(_WROTE
变量)以避免多次写入相同的行。
备注:
应该(嗯,除了做它应该做的)是"configurable"(代码很复杂,它正在走向meta :) 如果你愿意的话),意思是如果标记之间有变化,代码不应该改变(当然会有代码变化,但不是仅在变量定义中的功能部分)。让我详细说说:
- 如果您不再需要替换
Town=
字符串,那么您只需从_ALL
中删除_TOWN
:set _ALL=_PWD _ACCT _POST _LOC
. - 相反:如果你想添加一些其他标签(我们称之为Name),你必须创建一个新的环境变量:
set _NAME=Name=
并添加它到_ALL
:set _ALL=_PWD _ACCT _TOWN _POST _LOC _NAME
.
- 如果您不再需要替换
作为间接结果,我没有关注性能,所以它可能 运行 慢。无论如何,我试图将磁盘访问(非常慢)保持在最低限度(一个例子是当有 2
for
循环迭代文件内容时 - 假设每次迭代都需要磁盘访问;这可能不是真的,Win 有 IO 缓冲 - 它是外部缓冲。- 我"commented"把文件的最后一行去掉,避免覆盖原文件。如果需要该行为,只需删除开头的
rem
。
这是批处理代码:
@echo off
setlocal enabledelayedexpansion
set _INFILE="File.txt"
set _OUTFILE="NewFile.txt"
set _OLDFILE="OldFile.txt"
set _PWD=Pword=
set _ACCT=Account=
set _TOWN=Town=
set _POST=Postcode=
set _LOC=LocationChanged=
set _ALL=_PWD _ACCT _TOWN _POST _LOC
echo Parsing old file contents...
for /f "tokens=*" %%f in ('type !_OLDFILE!') do (
for %%g in (!_ALL!) do (
echo %%f | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
set %%gCONTENTS=%%f
)
)
)
copy nul %_OUTFILE%
echo Merging the old file contents into the new file...
set _WROTE=0
for /f "tokens=*" %%f in ('findstr /n "^^" !_INFILE!') do (
set _TMPVAR0=%%f
set _TMPVAR0=!_TMPVAR0:*:=!
for %%g in (!_ALL!) do (
echo !_TMPVAR0! | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
echo.!%%gCONTENTS!>>!_OUTFILE!
set _WROTE=1
)
)
if "!_WROTE!" equ "0" (
echo.!_TMPVAR0!>>!_OUTFILE!
) else (
set _WROTE=0
)
)
rem copy /-y %_OUTFILE% %_INFILE%
@EDIT0:使用@StevoStephenson 建议(作为问题片段的一部分),我将(第二个)外部 for
循环替换为 ('findstr /n "^^" !_INFILE!')
为了包括空行,所以第三条评论不再适用(删除)。还做了一些小的更改以允许在其路径中包含 SPACE 的文件。
它并不完美,但可能适合您:
@Echo Off
Setlocal EnableExtensions DisableDelayedExpansion
(Set InputFile=F:\EXCHANGE\Machine\File.txt)
(Set OutputFile=F:\EXCHANGE\File-New.txt)
(Set CopyFile=F:\EXCHANGE\OldMachine\OldFile.txt)
For /F "Delims=" %%I In (
'FindStr/B "Pword= Account= Town= LocationChanged= Postcode=" "%CopyFile%"'
) Do Set %%I
(For /F "Tokens=1-2* Delims=]=" %%I In ('Find /V /N ""^<"%InputFile%"') Do (
Echo(%%J|FindStr/B # || (If Defined %%J (Call Echo=%%J=%%%%J%%) Else (
If "%%J" NEq "" (Echo=%%J=%%K) Else (Echo=)))))>%OutputFile%
Timeout -1
EndLocal
Exit/B
我把删除和重命名留给你在最后添加。
这个解决方案应该比其他解决方案快得多。
它还将保留空行和包含 !
和 ^
.
它只需要一个 findstr
调用来收集所有单词的旧值。
第二个 findstr
确定 infile
中需要更新的所有行(按行号)。
@echo off
setlocal EnableDelayedExpansion
set "_INFILE=File.txt"
set "_OUTFILE=NewFile.txt"
set "_OLDFILE="OldFile.txt"
set "_WORDS=Pword= Account= Town= Postcode= LocationChanged="
REM *** get all values for the key words
for /F "tokens=1,* delims==" %%L in ('findstr "!_WORDS!" "!_OLDFILE!"') do (
for /F %%S in ("%%L") do (
set "word[%%S]=%%M"
)
)
REM *** Find all lines which needs an update
set wordIdx=0
for /F "tokens=1,2,* delims=:= " %%1 in ('findstr /n "!_WORDS!" "!_INFILE!"') do (
set "lines[!wordIdx!].line=%%1"
set "lines[!wordIdx!].word=%%2"
set "replace=!word[%%2]!"
set "lines[!wordIdx!].replace=!replace!"
set /a wordIdx+=1
)
REM *** copy the infile to the outfile
REM *** Replace only the lines which are marked by line numbers
echo Parsing old file contents...
set nextWordIdx=0
set /a searchLine=lines[!nextWordIdx!].line
set lineNo=0
setlocal DisableDelayedExpansion
(
for /f "tokens=*" %%L in ('findstr /n "^" "%_INFILE%"') do (
set "line=%%L"
set /a lineNo+=1
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if !lineNo! equ !searchLine! (
(echo(!line!!lines[0].replace!)
set /a nextWordIdx+=1
for /F %%R in ("!nextWordIdx!") do (
endlocal
set /a nextWordIdx=%%R
set /a searchLine=lines[%%R].line
)
) ELSE (
(echo(!line!)
endlocal
)
)
) > "!_OUTFILE!"