批量替换 xml 文件中特定文本之间的数字并加 1
Batch Replace and add 1 to a number between specific text in an xml file
好的,我正在尝试将批处理写入 "grab" 用户的序列号。序列号来自另一个系统,我们必须定期手动输入序列号。我希望用户 运行 批处理文件加 1 并替换旧号码,然后回显 "Your serial number is ##" 其中 ## 是新号码。我很确定我可以解决 Echo 部分并写入临时文件并覆盖旧文件,但我正在努力解决它的中间部分。
正在编辑的 xml 文件是:
<?xml version="1.0"?>
<BatchSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>10</LastSerialNumber>
</BatchSettings>
我需要更改的数字是 "lastserialnumber" <LastSerialNumber>10</LastSerialNumber>
之间的##。
我尝试了几种不同的方法,第一种方法很接近,但绝对没有雪茄。
@echo off
set /a "replace=10"
set /a "replaced=replace+1"
set "source=Test.txt"
set "target=Test2.txt"
setlocal enableDelayedExpansion
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
set "line=%%b"
if defined line set "line=!line:%replace%=%replaced%!"
echo(!line!
)
) > %target%
endlocal
问题是数字不会总是“10”,我也不想替换文件中所有其他“10”的实例。
我也试过了
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
@echo off
setlocal enableextensions disabledelayedexpansion
(for /f "delims=" %%a in (%SrcFile%
) do for /f "tokens=2 delims=1234567890 " %%b in ("%%~a"
) do if not "%%b"==":" (
echo(%%~a
) else for /f "tokens=1 delims= <LastSerialNumber>:" %%c in ("%%~a"
) do (
set /a "n=%%c+1"
setlocal enabledelayedexpansion
echo(<LastSerialNumber>!n!:
endlocal
)) > %OutFile%
但这不会增加数字并切断剩余的文本。
而这个只是一团糟,根本不起作用。
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
setlocal EnableDelayedExpansion
(for /F "delims=" %%a in ('findstr /I /L "<LastSerialNumber>" %SrcFile%') do (
set "line=%%a
set "line=!line:*<LastSerialNumber>=!"
for /F "delims=<" %%b in ("!line!") do echo %%b+1
)) > %oldnumber%
set "newnumber=%oldnumber%+1"
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %SrcFile%') do (
set "line=%%b"
if defined line set "line=!line:%oldnumber%=%newnumber%!"
echo(!line!
)
) > %OutFile%
endlocal
首先让我们快速浏览一下您的尝试:
- 失败,因为它只搜索
10
而根本不考虑 <LastSerialNumber>
标签。
- 失败是因为您尝试为
for /F
的 delims=
选项定义一个字符串,尽管它实际上定义了一组字符。
- 失败,因为您尝试重定向 (
>
) 到一个变量,这不起作用,您只能重定向到文件。此外,您尝试进行算术运算 (+1
),但这仅在使用 set /A
时有效。最后一节遇到了与方法1相同的问题。
这里有一个方法可以做到:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)
rem // Loop over all (non-empty) lines of the input file:
for /F "usebackq delims=" %%L in ("%_FILE%") do (
rem // Store current line string:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Split off opening tag from line string:
set "TEST=!LINE:*%_TAG%=!"
rem // Check whether opening tag has been found:
if not "!TEST!"=="!LINE!" (
rem // Opening tag found, hence split off closing tag:
for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
rem // Get extracted number and increment it:
set /A "NUM=%%S+1"
rem // Return rebuild line with incremented number:
echo( !_TAG!!NUM!^<%%T
)
) else (
rem // Opening tag not found, hence return original line:
echo(!LINE!
)
endlocal
)
endlocal
exit /B
鉴于批处理文件名为 inc-serial.bat
,请使用输入 XML 文件作为命令行参数调用它(比如当前目录中的 test.xml
):
inc-serial.bat "test.xml"
要将输出写入另一个文件,请使用重定向 (>
):
inc-serial.bat "test.xml" > "test_NEW.xml"
好的,我终于得到了我需要的代码。只需要设置目录,出于显而易见的原因,我在这里已经离开了。非常感谢 aschipfl 的帮助。
此代码将序列号加一索引,写入临时文件,然后写回并删除临时文件。然后显示新的序列号。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=BatchTest.xml" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)
rem // Loop over all (non-empty) lines of the input file:
(for /F "usebackq delims=" %%L in ("%_FILE%") do (
rem // Store current line string:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Split off opening tag from line string:
set "TEST=!LINE:*%_TAG%=!"
rem // Check whether opening tag has been found:
if not "!TEST!"=="!LINE!" (
rem // Opening tag found, hence split off closing tag:
for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
rem // Get extracted number and increment it:
set /A "NUM=%%S+1"
rem // Return rebuild line with incremented number:
echo( !_TAG!!NUM!^<%%T
)
) else (
rem // Opening tag not found, hence return original line:
echo(!LINE!
)
endlocal
))>BatchTestTmp.xml
copy /v /y "BatchTestTmp.xml" "BatchTest.xml" & cls
del "BatchTestTmp.xml"
set "LINE=4"
(for /f "usebackq delims=" %%a in (`more +%LINE% BatchTest.xml`) DO (
set result=%%a
setlocal EnableDelayedExpansion
echo Your Serial Number is !result:~20,2!
pause
goto :leave
))
:leave
endlocal
exit /B
虽然旧 Batch 仍然可以解决这个问题令人印象深刻,但它在设计时并没有考虑到对 XML 的支持。所以我会推荐一个原生支持 XML 的工具,比如 xidel.
“ECHO 部分”很简单:
xidel -s input.xml -e "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1, is the new number')"
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'"
Your serial number is 10 where 11 is the new number
编辑中:
xidel -s input.xml -e "x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1})" --output-format=xml --output-declaration="<?xml version=\"1.0\"?>"
<?xml version="1.0"?>
<BatchSettings xsi="http://www.w3.org/2001/XMLSchema-instance" xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>11</LastSerialNumber>
</BatchSettings>
要替换输入文件,只需添加 --in-place
:
xidel -s --in-place input.xml ...
要将输出写入另一个文件,您当然可以将输出重定向到一个...
xidel -s input.xml ... > input_edit.xml
...或者您可以使用 file:write()
:
xidel -s input.xml -e "file:write('input_edit.xml',x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}),{'omit-xml-declaration':false()})"
然而,file:write()
在这种情况下输出 <?xml version="1.0" encoding="UTF-8"?>
。如果你想要原始声明(没有“编码”位),那么你必须使用 file:write-text()
并手动添加它。这与“回声部分”相结合:
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" -e "file:write-text('input_edit.xml',('<?xml version=\"1.0\"?>',serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))))"
Your serial number is 10 where 11 is the new number
第一个提取查询打印消息字符串。第二个将输出写入 'input_edit.txt'.
美化的最后一个 Xidel 命令(带有必要的转义字符):
xidel -s input.xml ^
-e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" ^
-e ^"^
file:write-text(^
'input_edit.xml',^
(^
'^<?xml version=\"1.0\"?^>',^
serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))^
)^
)^
"
好的,我正在尝试将批处理写入 "grab" 用户的序列号。序列号来自另一个系统,我们必须定期手动输入序列号。我希望用户 运行 批处理文件加 1 并替换旧号码,然后回显 "Your serial number is ##" 其中 ## 是新号码。我很确定我可以解决 Echo 部分并写入临时文件并覆盖旧文件,但我正在努力解决它的中间部分。 正在编辑的 xml 文件是:
<?xml version="1.0"?>
<BatchSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>10</LastSerialNumber>
</BatchSettings>
我需要更改的数字是 "lastserialnumber" <LastSerialNumber>10</LastSerialNumber>
之间的##。
我尝试了几种不同的方法,第一种方法很接近,但绝对没有雪茄。
@echo off
set /a "replace=10"
set /a "replaced=replace+1"
set "source=Test.txt"
set "target=Test2.txt"
setlocal enableDelayedExpansion
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
set "line=%%b"
if defined line set "line=!line:%replace%=%replaced%!"
echo(!line!
)
) > %target%
endlocal
问题是数字不会总是“10”,我也不想替换文件中所有其他“10”的实例。
我也试过了
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
@echo off
setlocal enableextensions disabledelayedexpansion
(for /f "delims=" %%a in (%SrcFile%
) do for /f "tokens=2 delims=1234567890 " %%b in ("%%~a"
) do if not "%%b"==":" (
echo(%%~a
) else for /f "tokens=1 delims= <LastSerialNumber>:" %%c in ("%%~a"
) do (
set /a "n=%%c+1"
setlocal enabledelayedexpansion
echo(<LastSerialNumber>!n!:
endlocal
)) > %OutFile%
但这不会增加数字并切断剩余的文本。 而这个只是一团糟,根本不起作用。
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
setlocal EnableDelayedExpansion
(for /F "delims=" %%a in ('findstr /I /L "<LastSerialNumber>" %SrcFile%') do (
set "line=%%a
set "line=!line:*<LastSerialNumber>=!"
for /F "delims=<" %%b in ("!line!") do echo %%b+1
)) > %oldnumber%
set "newnumber=%oldnumber%+1"
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %SrcFile%') do (
set "line=%%b"
if defined line set "line=!line:%oldnumber%=%newnumber%!"
echo(!line!
)
) > %OutFile%
endlocal
首先让我们快速浏览一下您的尝试:
- 失败,因为它只搜索
10
而根本不考虑<LastSerialNumber>
标签。 - 失败是因为您尝试为
for /F
的delims=
选项定义一个字符串,尽管它实际上定义了一组字符。 - 失败,因为您尝试重定向 (
>
) 到一个变量,这不起作用,您只能重定向到文件。此外,您尝试进行算术运算 (+1
),但这仅在使用set /A
时有效。最后一节遇到了与方法1相同的问题。
这里有一个方法可以做到:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)
rem // Loop over all (non-empty) lines of the input file:
for /F "usebackq delims=" %%L in ("%_FILE%") do (
rem // Store current line string:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Split off opening tag from line string:
set "TEST=!LINE:*%_TAG%=!"
rem // Check whether opening tag has been found:
if not "!TEST!"=="!LINE!" (
rem // Opening tag found, hence split off closing tag:
for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
rem // Get extracted number and increment it:
set /A "NUM=%%S+1"
rem // Return rebuild line with incremented number:
echo( !_TAG!!NUM!^<%%T
)
) else (
rem // Opening tag not found, hence return original line:
echo(!LINE!
)
endlocal
)
endlocal
exit /B
鉴于批处理文件名为 inc-serial.bat
,请使用输入 XML 文件作为命令行参数调用它(比如当前目录中的 test.xml
):
inc-serial.bat "test.xml"
要将输出写入另一个文件,请使用重定向 (>
):
inc-serial.bat "test.xml" > "test_NEW.xml"
好的,我终于得到了我需要的代码。只需要设置目录,出于显而易见的原因,我在这里已经离开了。非常感谢 aschipfl 的帮助。 此代码将序列号加一索引,写入临时文件,然后写回并删除临时文件。然后显示新的序列号。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=BatchTest.xml" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)
rem // Loop over all (non-empty) lines of the input file:
(for /F "usebackq delims=" %%L in ("%_FILE%") do (
rem // Store current line string:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Split off opening tag from line string:
set "TEST=!LINE:*%_TAG%=!"
rem // Check whether opening tag has been found:
if not "!TEST!"=="!LINE!" (
rem // Opening tag found, hence split off closing tag:
for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
rem // Get extracted number and increment it:
set /A "NUM=%%S+1"
rem // Return rebuild line with incremented number:
echo( !_TAG!!NUM!^<%%T
)
) else (
rem // Opening tag not found, hence return original line:
echo(!LINE!
)
endlocal
))>BatchTestTmp.xml
copy /v /y "BatchTestTmp.xml" "BatchTest.xml" & cls
del "BatchTestTmp.xml"
set "LINE=4"
(for /f "usebackq delims=" %%a in (`more +%LINE% BatchTest.xml`) DO (
set result=%%a
setlocal EnableDelayedExpansion
echo Your Serial Number is !result:~20,2!
pause
goto :leave
))
:leave
endlocal
exit /B
虽然旧 Batch 仍然可以解决这个问题令人印象深刻,但它在设计时并没有考虑到对 XML 的支持。所以我会推荐一个原生支持 XML 的工具,比如 xidel.
“ECHO 部分”很简单:
xidel -s input.xml -e "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1, is the new number')"
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'"
Your serial number is 10 where 11 is the new number
编辑中:
xidel -s input.xml -e "x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1})" --output-format=xml --output-declaration="<?xml version=\"1.0\"?>"
<?xml version="1.0"?>
<BatchSettings xsi="http://www.w3.org/2001/XMLSchema-instance" xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>11</LastSerialNumber>
</BatchSettings>
要替换输入文件,只需添加 --in-place
:
xidel -s --in-place input.xml ...
要将输出写入另一个文件,您当然可以将输出重定向到一个...
xidel -s input.xml ... > input_edit.xml
...或者您可以使用 file:write()
:
xidel -s input.xml -e "file:write('input_edit.xml',x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}),{'omit-xml-declaration':false()})"
然而,file:write()
在这种情况下输出 <?xml version="1.0" encoding="UTF-8"?>
。如果你想要原始声明(没有“编码”位),那么你必须使用 file:write-text()
并手动添加它。这与“回声部分”相结合:
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" -e "file:write-text('input_edit.xml',('<?xml version=\"1.0\"?>',serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))))"
Your serial number is 10 where 11 is the new number
第一个提取查询打印消息字符串。第二个将输出写入 'input_edit.txt'.
美化的最后一个 Xidel 命令(带有必要的转义字符):
xidel -s input.xml ^
-e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" ^
-e ^"^
file:write-text(^
'input_edit.xml',^
(^
'^<?xml version=\"1.0\"?^>',^
serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))^
)^
)^
"