You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

批处理脚本中set str1=#%str1%的含义及含<>时误覆写文件的原因

批处理脚本处理含特殊字符日志时的文件操作问题解决

问题说明

写了个批处理脚本,用来逐行读取日志文件,提取错误行的LineNo(行号)和ErrorNo(错误号),生成Visual Studio输出窗口兼容的格式,方便直接跳转错误代码。但遇到日志行里带>或<这类特殊字符时,脚本会意外执行文件操作:比如示例1会创建名为(parameter的文件,示例2会覆写abc.xyz文件。注释掉set str2=#%str1%能停止文件操作,但会导致无法正确识别LineNo和ErrorNo。

脚本调用方式:MyBat.bat build.log,需要从&rem.之前的子串获取错误代码前的正确索引(错误1对应17,错误2对应16),同时避免触发文件覆写。

原问题脚本

@echo off
for /F "tokens=* delims=" %%A in (Build.log) do call :Process "%%A" %1
goto :eof

:Process
setlocal enableDelayedExpansion
  call :GetLineError %1 LineNo ErrorNo
  echo."%2(!LineNo!):error(!ErrorNo!):" %1
goto :eof

:GetLineError
setlocal enableDelayedExpansion

set string="%~1"
rem set "string=%string:<=#%"
rem set "string=%string:>=#%"
set "find_index_of=error code:"

call :indexof %string% "%find_index_of%" index

:indexof [%1 - string ; %2 - find index of ; %3 - if defined will store the result in variable with same name]

setlocal enableDelayedExpansion     
set "str=%~1"
set "str1=!str:%~2=&rem.!"

REM 这行是问题根源
set str2=#%str1%

call :strLen str2 strlen
rem echo.String is %strlen% characters long
(endlocal & set "%3=%strlen%")
exit /b

:strLen
setlocal enabledelayedexpansion

:strLen_Loop

   if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof

原问题输出

mytool: line 63,
mytool: line 63, &rem.  541 ,    can not omit value. ===> (parameter no 1)
 line 63,
#mytool: line 63,
#mytool: line 63,
 line 63,
74
mytool: line 0,
mytool: line 0, &rem.    5 ,    file is abnormal. ===> C:\Projects\abc_DemoSampleExample\abc\my\abc.xyz
 line 0,
#mytool: line 0,
#mytool: line 0,
 line 0,
122

问题根源

问题出在set str2=#%str1%这行:这里用百分号%展开变量str1,当str1里包含>、<、&这类特殊字符时,会被CMD解析成命令执行。比如日志里的> (parameter no 1)会被当成重定向命令,从而创建新文件;> C:\...\abc.xyz会直接覆写目标文件。

修复方案

核心思路是全程使用延迟扩展(!变量!)替代百分号展开,避免CMD将特殊字符解析为命令:

  • 将set str2=#%str1%改为set "str2=#!str1!",用延迟扩展展开str1,同时用引号包裹赋值语句防止特殊字符逃逸。
  • 调整:indexof函数里的变量处理逻辑,确保所有字符串操作都用延迟扩展。
  • 移除对特殊字符的临时替换(注释掉的set "string=%string:<=#%"),因为延迟扩展已经能处理这些字符。

修复后的完整脚本

@echo off
for /F "tokens=* delims=" %%A in (Build.log) do call :Process "%%A" %1
goto :eof

:Process
setlocal enableDelayedExpansion
  call :GetLineError %1 LineNo ErrorNo
  echo."%2(!LineNo!):error(!ErrorNo!):" %1
endlocal
goto :eof

:GetLineError
setlocal enableDelayedExpansion
set "string=%~1"
set "find_index_of=error code:"

call :indexof "!string!" "%find_index_of%" index

REM 提取行号和错误号的逻辑
set "linePart=!string:*line =!"
for /F "tokens=1 delims=," %%L in ("!linePart!") do set "LineNo=%%L"
set "errorPart=!string:*error code: =!"
for /F "tokens=1 delims=," %%E in ("!errorPart!") do set "ErrorNo=%%E"

endlocal & set "%2=%LineNo%" & set "%3=%ErrorNo%"
exit /b

:indexof [%1 - 目标字符串; %2 - 要查找的子串; %3 - 存储结果的变量名]
setlocal enableDelayedExpansion
set "str=%~1"
set "subStr=%~2"

set "str1=!str:%subStr%=&rem.!"
set "str2=#!str1!"

call :strLen str2 strlen
set /A "index=strlen-1"

endlocal & set "%3=%index%"
exit /b

:strLen
setlocal enabledelayedexpansion
set "len=0"
:strLen_Loop
if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
endlocal & set %2=%len%
goto :eof

修复效果

  • 不再触发任何意外的文件创建/覆写操作。
  • 能正确计算error code:之前的子串长度(错误1得到17,错误2得到16)。
  • 生成的输出格式兼容Visual Studio,可直接跳转错误行。

内容的提问来源于stack exchange,提问作者Peeyush

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:21:58