您找遍全网可能都找不到的解决方案
1 背景
最近迷恋上了bat脚本,以前没这么深入用,对它没啥感情。但是最近是近乎疯狂的迷上了它。
但是越是用的深入,越是发现各种稀奇古怪的事情:
就比如对特殊符号的处理,比如想要替换字符串里头的感叹号和百分号,死活就是成功不了。
而这类的功能换用javaScript/java/python都会非常简单。
没办法,总不能因为人家有弱点,我就弃用吧!
所以死磕开始...
2 问题描述
要替换的内容在txt文件里头(替换的字符串不包含特殊符号),然后逐行读取的时候,我开启了变量延迟,然后呢,替换之后,发现txt里头原本有的特殊符号!丢失了。查了下因为!是变量延迟开启时候的特殊符号。
于是我关闭了变量延迟使用call 的变量延迟的实现方案,但是这个时候又出错了,因为我的文本里头有><这类标签的符号,查了下这类也是特殊符号,要用需要转义。
综上,奔溃呀!!!
所以稍微总结下:
使用!var!这种变量延迟,可以解决除了!特殊符号外的所有特殊符号,
而使用call 这种变量延迟,可以解决!特殊符号。
另外有一个前提,就是要替换的内容肯定不会和!同行。
所以其实要做的就简单了。只要逐行判断是否有!,如果有,就跳过,不替换。如果没有,就开启!var!模式的变量延迟,就替换。但是,判断某一行有没有!,就是我在为期三天的排查过程的瓶颈了。找不到可用的。
市面上能找得到的解决方案都是通过echo %var% | findstr....
但是这种模式会导致findstr之前,就让var里头的感叹号丢失了。
3 处理字符串里头的!%等特殊符号
笔者也是翻看了全网的很多资料。但是在我把全网翻个底朝天之前,在一个疙瘩角落里头找到了一个曲线救国的方案。
既然通过echo %var% | findstr....行不通。
但是通过findStr直接读取文本文件是不会有问题的。
所以作者采用逐行读取文本内容,然后读取之后,将当前行写入到一个临时文件里头,再采用findstr来判断是否有!:
@echo off
Setlocal enableDelayedExpansion
rem 逐行读取
for /f "delims=" %%i in (a.txt) do (
rem 判断当前行是否包含有特殊符号% !
rem 第一步先写入到文件里头
echo %%i > special.tmp
rem 第二步通过文件来判断
call :hasSpecialWord
if "!specialWordNo!"=="" (
echo 不存在特殊符号
)^
else (
echo 存在特殊符号!specialWordNo!
)
)
pause
rem 判断是否含有% ! 特殊符号只要被进行赋值等之类操作,都会没掉,因此只能只能通过文件来处理
:hasSpecialWord
for /f "delims=:" %%a in ('findstr /n "%% ^!" special.tmp') do set specialWordNo=%%a
goto end
:end
上面的demo成功之后,就开始解决前面的背景问题:
@echo off
rem 案例要求:对a.txt进行如下操作: 替换里头的xml为wlt,a.txt里头包含有%和!
Setlocal enableDelayedExpansion
rem 创建一个空白文件
1 2>nul > a.txt.bak
for /f "delims=" %%i in (a.txt) do (
rem 每次循环前,先开启变量延迟
Setlocal enableDelayedExpansion
rem 判断当前行是否包含有特殊符号% !
rem 第一步先写入到文件里头
echo %%i > special.tmp
rem 第二步通过文件来判断
call :hasSpecialWord
if "!specialWordNo!"=="" (
set tmp=%%i
set tmp=!tmp:xml=wlt!
echo !tmp! >> a.txt.bak
)^
else (
rem 当含有特殊符号的时候,关闭变量延迟
Setlocal disableDelayedExpansion
echo %%i >> a.txt.bak
)
)
del a.txt
ren a.txt.bak a.txt
echo 总结:
echo 开启变量延迟 直接输出会丢失感叹号
echo 关闭变量延迟 直接输出可以全部保留
echo 开启变量延迟 变量替换操作在变量含有感叹号的时候会出错
echo 关闭变量延迟 变量替换操作在变量含有左尖括号或者右尖括号时会出错
pause
rem 特殊符号只要被进行赋值等之类操作,都会没掉,因此只能只能通过文件来处理
:hasSpecialWord
for /f "delims=:" %%a in ('findstr /n "%% ^!" special.tmp') do set specialWordNo=%%a
goto end
:end
附上代码里头使用的a.txt:
<xml></xml>
a=1%
b!!!!=null<wlt></wlt>
运行代码后得到a.txt为:
<wlt></wlt>
a=1%
b!!!!=null<wlt></wlt>
赠送给新一代农民工:每天学习一点点,天天发现新大陆
20220722更新
应读者要求,将评论区的内容更新到这边:
读者遇到一个问题:
<a>
<!--This is a test file -->
17302 xml
</a>
转完就变成了
<a>
<--This is a test file -->
17302 wlt
</a>
我提出了一个解决方案:
@echo off
setlocal enabledelayedexpansion
rem 清空1.bak
1 2>nul > 1.bak
for /f "delims=" %%i in (c.txt) do (
setlocal disabledelayedexpansion
rem 如果包含感叹号,就写入1.bak
echo "%%i" | findstr "!" >nul &&( echo %%i >> 1.bak ) || (
endlocal
setlocal enabledelayedexpansion
set tmpFileContent=%%i
set tmpFileContent=tmpFileContent:xml=wlt!
echo !tmpFileContent! >> 1.bak
)
endlocal
)