批处理:如何在解析输出时纠正变量覆盖错误行为

Thu*_*per 4 windows for-loop cmd batch-file wmic

在批处理文件中,我正在检查Baseboard信息,具有以下内容:
BaseboardCheck.cmd

@echo off
setlocal enabledelayedexpansion

for /f "tokens=1,2* delims==" %%a in ('wmic baseboard get /format:list') DO ( 

    if ["%%a"] EQU ["Product"] (
        set PlatformInfo=%%b

        if defined PlatformInfo (
            echo.!PlatformInfo!
            echo.!PlatformInfo!This overwrites the variable
        )
    )

    if ["%%a"] EQU ["Version"] (
        set BaseboardVersion=%%b

        if defined BaseboardVersion (
            echo.!BaseboardVersion!
            echo.!BaseboardVersion!This overwrites the variable
        )
    )   
)
Run Code Online (Sandbox Code Playgroud)

上面的问题:回声时变量会被覆盖而不是附加到变量上.
输出:

DX79SI
This overwrites the variable
AAG28808-600
This overwrites the variable
Run Code Online (Sandbox Code Playgroud)

我想要的是:

DX79SI
DX79SIThis overwrites the variable
AAG28808-600
AAG28808-600This overwrites the variable
Run Code Online (Sandbox Code Playgroud)

我已经花了几个小时(并将继续这样做),但我希望其他人遇到这个问题.我希望遇到这个解析问题的其他人可以在将来避免它.

由此产生的另一个问题是它似乎打破了条件逻辑.

更新
在所有帮助之后,我想出了这个解决方案:

for /f "skip=2 tokens=1,2 delims=," %%a in ('wmic baseboard get Product^,Version^,Width /format:csv') do (

    set Platform=%%a
    set BaseboardVersion=%%b
)
echo.Platform: %Platform% 
echo.Version %BaseboardVersion%.
Run Code Online (Sandbox Code Playgroud)

Mof*_*ofi 7

哇,真的很难找到这里发生的事情.

首先,我无法相信批处理文件执行会发生什么.

经过多次试验,我在Windows XP SP3 x86上执行了

wmic.exe baseboard get /format:list > Output.txt
Run Code Online (Sandbox Code Playgroud)

Output.txt查看文件管理器Total Commander的查看器.我在顶部看到2条空行,但这并不重要.所以我继续进行其他试验.

后来我Output.txt在文本编辑器UltraEdit中打开并立即在状态栏U-DOS上看到输出文件是用UTF-16小端编码的,具有DOS行终止符.我切换到十六进制编辑模式,可以看到

00000000h: FF FE 0D 00 0A 00 0D 00 0A 00 43 00 61 00 70 00 ; ÿþ........C.a.p.
00000010h: 74 00 69 00 6F 00 6E 00 3D 00                   ; t.i.o.n.=.
Run Code Online (Sandbox Code Playgroud)

因此输出文件确实是具有UTF-16 LE BOM的Unicode文件.没有CR CR LF.所有行终止都是正确的CR LF对(回车+换行).

现在我在Stack Overflow上搜索包含单词wmic Unicode的批处理文件的问题,发现cmd以某种方式将中文文本作为输出.

dbenham接受的答案并不好,因为它创建了ANSI版本的Unicode输出wmic.exe,但ANSI文件现在确实包含0D 0D 0A(= CR CR LF).

Dharma Leonardi作为使用命令的解决方案更好的答案type是将Unicode输出正确转换为ANSI输出,只要输出不包含ANSI代码页中不可用的字符.

但是在更改批处理代码以处理wmic.exe带有ANSI编码的输出之后,if defined BaseboardVersion评估的行始终为true,尽管我看不到该变量BaseboardVersion包含任何数据,因此下一行导致显示回显状态.

我花了一些时间才发现插入set > Variables.txt上面的条件并查看这个文件,在我的计算机上,版本字符串只是一个空格字符.值版本是所有密钥的唯一的价值,而不只包含一个空格等号的字符串权.

这是最终在我的计算机上工作并产生预期输出的批处理文件:

@echo off
setlocal enabledelayedexpansion
wmic.exe /OUTPUT:"%TEMP%\UnicodeData.tmp" baseboard get /format:list
for /f "usebackq tokens=1,2* delims==" %%a in (`type "%TEMP%\UnicodeData.tmp"`) do (

    if "%%a" == "Product" (
        if not "%%b" == "" if not "%%b" == " " (
            set "PlatformInfo=%%b"
            echo !PlatformInfo!
            echo !PlatformInfo!This overwrites the variable
        )
    ) else if "%%a" == "Version" (
        if not "%%b" == "" if not "%%b" == " " (
            set "BaseboardVersion=%%b"
            echo !BaseboardVersion!
            echo !BaseboardVersion!This overwrites the variable
        )
    )
)
del "%TEMP%\UnicodeData.tmp"
endlocal
Run Code Online (Sandbox Code Playgroud)

  • 仅当wmic使用C运行时标准I/O写入*控制台*或*管道*而不是磁盘文件时,才会发生CRCRLF.它将以CRLF结尾的字符串写入文本模式CRT文件描述符,后者又将LF转换为CRLF,从而产生CRCRLF.写入磁盘文件需要一个不同的路径来调用自定义类方法`wmic!CFileOutputStream :: Write`,它写入字符串而不将LF的文本模式转换为CRLF. (4认同)
  • 就像我说的那样,当输出被定向到磁盘文件(即> List.txt)时,CRLF => CRCRLF不会发生,因为这在代码中采用了不使用C运行时标准I / O的单独路径。通常,人们调用wmic.exe时不会将输出定向到管道,例如,在CMD for / f循环中或在more.com中。 (3认同)
  • 我的意思是不是[`FILE_TYPE_DISK`](https://msdn.microsoft.com/zh-cn/library/aa364960)的文件,该文件可能是“ FILE_TYPE_CHAR”(例如控制台)或“ FILE_TYPE_PIPE”。当wmic正在写入控制台或管道时,请附加调试器。您会看到它将字符串传递给以CRCRLF结尾的`WriteFile`。这是通过使用文本模式的文件描述符将以CRLF结尾的字符串传递到fprintf(C标准I / O)来实现的。在这种情况下,控制台和more.com都具有弹性。CMD的“ for / f”循环剥离最终的CRLF,但是CR保留在每一行上。 (2认同)
  • 顺便说一句,wmic实际上将OEM(例如代码页437或850)写入管道或控制台,而不是ANSI(例如代码页1252)。在某些语言环境中,这些是相同的,但我认为在所有西方语言环境中,它们都是不同的代码页。 (2认同)

MC *_* ND 4

是的,你有问题,但问题不是你想的那样。

wmic有一个特殊的行为:在每行输出的末尾有一个附加的回车符,即每行以0x0d 0x0d 0x0a

这个附加的回车符存储在变量内,当回显到控制台时,您将获得数据和回车符,因此,如果变量后面跟着更多文本,因为光标已位于行的开头(回车),此文本将在先前回显的数据上回显。

怎么解决?

@echo off
setlocal enabledelayedexpansion

for /f "tokens=1,* delims==" %%a in ('wmic baseboard get /format:list') DO ( 


    if ["%%a"] EQU ["Product"] (
        for /f "delims=" %%c in ("%%b") do set "PlatformInfo=%%c"

        if defined PlatformInfo (
            echo(!PlatformInfo!
            echo(!PlatformInfo!This does not overwrite the variable
        )
    )

    if ["%%a"] EQU ["Version"] (
        for /f "delims=" %%c in ("%%b") do set "BaseboardVersion=%%c"

        if defined BaseboardVersion (
            echo(!BaseboardVersion!
            echo(!BaseboardVersion!This does not overwrite the variable
        )
    )   
)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,在不改变代码逻辑的情况下,for /f可以使用附加命令来删除附加回车符