cmd批处理for循环处理wmic输出含<CR>异常行的方法
问题本质
wmic命令输出默认采用UTF-16 LE编码,for /f在转码解析其输出时,会在所有有效行结束后多解析出一行仅包含孤立<CR>(ASCII 0x0D,即回车符\r)的异常内容。该字符不属于常规空值,因此普通的if [%%G] neq []空行判断逻辑无法识别拦截,才会出现ECHO is off.提示、打印内容被回车覆盖错位的现象。
注意:Windows/DOS系统标准行终止符为<CR><LF>(回车+换行)组合,该异常行仅含单独的
可用方案
以下方案均兼容for /f自定义tokens、delims参数的文本处理场景,按易用性排序:
嵌套for循环自动剥离(首推,兼容性最强)
for /f解析字符串参数时,会自动丢弃行尾孤立存在的字符,只需在原有循环内嵌套一层无额外参数的 for /f即可完成清洗,无需额外定义常量、开启延迟扩展,也不会破坏正常行内容。
示例代码:for /f "delims=" %%a in ('wmic os get version') do ( :: 内层循环自动剥离冗余<CR> for /f "tokens=* delims=" %%G in ("%%a") do ( :: 此处可自由添加自定义tokens、delims逻辑处理内容 echo %%G ) )运行后只会输出两行有效内容:列名
Version和系统版本号,无多余空提示、无内容错位。字符串替换移除
先通过特殊方式定义值为0x0D的CR常量,再用变量替换把所有
从行内容中移除,之后即可用常规非空判断过滤冗余行。
示例代码::: 定义CR常量,以下代码行请勿添加多余空格 set "CR=" for /f "delims=" %%c in ('copy /Z "%~f0" nul') do set "CR=%%c" for /f "delims=" %%G in ('wmic os get version') do ( set "line=%%G" setlocal enabledelayedexpansion :: 移除行内所有<CR>字符 set "line=!line:%CR%=!" :: 非空判断后输出 if defined line echo !line! endlocal )该方案缺点是需要开启延迟变量扩展,若待处理内容包含感叹号
!会出现解析异常,适配多token场景时需要额外做变量转换,适配成本较高。if条件直接识别纯
行 提前定义CR常量后,直接判断循环变量值是否等于单个
,匹配则跳过该行即可。
示例代码::: 定义CR常量,以下代码行请勿添加多余空格 set "CR=" for /f "delims=" %%c in ('copy /Z "%~f0" nul') do set "CR=%%c" for /f "delims=" %%G in ('wmic os get version') do ( :: 匹配纯<CR>行直接跳过 if not "%%G" == "%CR%" echo %%G )该方案缺点是必须提前定义CR常量,若自定义delims拆分多token,需要对每个拆分后的变量单独做判断,代码冗余度高。
避坑提示
不要尝试将
设置为 for /f的delims分隔符来过滤异常行,cmd的for /f语法不支持传入0x0D作为自定义分隔符,该写法不会生效。
内容的提问来源于stack exchange,提问作者john v kumpf

