且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何删除批处理文件中的新行?

更新时间:2023-12-03 21:30:52

%SystemRoot%\System32\systeminfo.exe的依赖于OS语言的输出为 ASCII 字符).

The OS language dependent output of %SystemRoot%\System32\systeminfo.exe is character encoded in ASCII/ANSI/OEM which means one byte per character using the code page as displayed on running in a command prompt window chcp. The code page depends on the country (region) configured for the account used to run the batch file. The code page does not really matter as long as the data of interest do not contain characters with a code value greater 127 (non-ASCII character).

findstr过滤后的systeminfo的输出为二进制,带有左移冒号的文件中的十六进制偏移量,字节的十六进制值以及分号后的ASCII表示形式:

The output of systeminfo filtered by findstr is in binary with hexadecimal offset in file left to colon, hexadecimal values of the bytes, and their ASCII representation after the semicolon:

0000h: 48 6F 73 74 20 4E 61 6D 65 3A 20 20 20 20 20 20 ; Host Name:      
0010h: 20 20 20 20 20 20 20 20 20 20 20 50 47 56 2D 50 ;            PGV-P
0020h: 46 31 36 35 48 4E 4E 0D 0A 4F 53 20 4E 61 6D 65 ; F165HNN..OS Name
0030h: 3A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 ; :               
0040h: 20 20 20 20 4D 69 63 72 6F 73 6F 66 74 20 57 69 ;     Microsoft Wi
0050h: 6E 64 6F 77 73 20 31 30 20 50 72 6F 0D 0A 4F 53 ; ndows 10 Pro..OS
0060h: 20 56 65 72 73 69 6F 6E 3A 20 20 20 20 20 20 20 ;  Version:       
0070h: 20 20 20 20 20 20 20 20 20 31 30 2E 30 2E 31 38 ;          10.0.18
0080h: 33 36 33 20 4E 2F 41 20 42 75 69 6C 64 20 31 38 ; 363 N/A Build 18
0090h: 33 36 33 0D 0A 4F 72 69 67 69 6E 61 6C 20 49 6E ; 363..Original In
00a0h: 73 74 61 6C 6C 20 44 61 74 65 3A 20 20 20 20 20 ; stall Date:     
00b0h: 37 2F 32 32 2F 32 30 31 39 2C 20 36 3A 32 38 3A ; 7/22/2019, 6:28:
00c0h: 30 31 20 50 4D 0D 0A 53 79 73 74 65 6D 20 4D 61 ; 01 PM..System Ma
00d0h: 6E 75 66 61 63 74 75 72 65 72 3A 20 20 20 20 20 ; nufacturer:     
00e0h: 20 20 4C 45 4E 4F 56 4F 0D 0A 53 79 73 74 65 6D ;   LENOVO..System
00f0h: 20 4D 6F 64 65 6C 3A 20 20 20 20 20 20 20 20 20 ;  Model:         
0100h: 20 20 20 20 20 32 30 4A 4D 30 30 30 39 55 53 0D ;      20JM0009US.
0110h: 0A 53 79 73 74 65 6D 20 54 79 70 65 3A 20 20 20 ; .System Type:   
0120h: 20 20 20 20 20 20 20 20 20 20 20 20 78 36 34 2D ;             x64-
0130h: 62 61 73 65 64 20 50 43 0D 0A 42 49 4F 53 20 56 ; based PC..BIOS V
0140h: 65 72 73 69 6F 6E 3A 20 20 20 20 20 20 20 20 20 ; ersion:         
0150h: 20 20 20 20 20 4C 45 4E 4F 56 4F 20 4E 31 51 45 ;      LENOVO N1QE
0160h: 54 38 37 57 20 28 31 2E 36 32 20 29 2C 20 32 2F ; T87W (1.62 ), 2/
0170h: 32 37 2F 32 30 32 30 0D 0A 54 6F 74 61 6C 20 50 ; 27/2020..Total P
0180h: 68 79 73 69 63 61 6C 20 4D 65 6D 6F 72 79 3A 20 ; hysical Memory: 
0190h: 20 20 20 20 38 2C 30 37 32 20 4D 42 0D 0A       ;     8,072 MB..

%SystemRoot%\System32\wbem\wmic.exe的输出始终为 Unicode 使用字节顺序标记(BOM).因此,两个使用过的wmic命令行的输出每个字符有两个字节.

The output of %SystemRoot%\System32\wbem\wmic.exe is always Unicode encoded using UTF-16 Little Endian encoding with byte order mark (BOM). So the output by the two used wmic command lines is with two bytes per character.

命令行wmic bios get serialnumber /Format:list以二进制形式生成输出:

The command line wmic bios get serialnumber /Format:list produces in binary the output:

0000h: FF FE 0D 00 0A 00 0D 00 0A 00 53 00 65 00 72 00 ; ÿþ........S.e.r.
0010h: 69 00 61 00 6C 00 4E 00 75 00 6D 00 62 00 65 00 ; i.a.l.N.u.m.b.e.
0020h: 72 00 3D 00 50 00 46 00 31 00 36 00 35 00 48 00 ; r.=.P.F.1.6.5.H.
0030h: 4E 00 4E 00 0D 00 0A 00 0D 00 0A 00 0D 00 0A 00 ; N.N.............

前两个字节FF FE是UTF-16 Little Endian的字节顺序标记.每个ASCII字符都用两个字节(16位)编码,高字节的值为0.换行符是回车符(0D 00)和换行符(0A 00).首先输出两个空行,然后输出包含感兴趣的数据的行,最后再输出两个空行.

The first two bytes FF FE is the byte order mark for UTF-16 Little Endian. Each ASCII character is encoded with two bytes (16 bits) with high byte having value 0. The newline characters are carriage return (0D 00) and line-feed (0A 00). There are two empty lines output first, then the line with the data of interest, and finally once again two empty lines.

命令行wmic cpu get name以二进制形式生成输出:

The command line wmic cpu get name produces in binary the output:

0000h: FF FE 0D 00 0A 00 0D 00 0A 00 4E 00 61 00 6D 00 ; ÿþ........N.a.m.
0010h: 65 00 3D 00 49 00 6E 00 74 00 65 00 6C 00 28 00 ; e.=.I.n.t.e.l.(.
0020h: 52 00 29 00 20 00 43 00 6F 00 72 00 65 00 28 00 ; R.). .C.o.r.e.(.
0030h: 54 00 4D 00 29 00 20 00 69 00 35 00 2D 00 36 00 ; T.M.). .i.5.-.6.
0040h: 33 00 30 00 30 00 55 00 20 00 43 00 50 00 55 00 ; 3.0.0.U. .C.P.U.
0050h: 20 00 40 00 20 00 32 00 2E 00 34 00 30 00 47 00 ;  .@. .2...4.0.G.
0060h: 48 00 7A 00 0D 00 0A 00 0D 00 0A 00 0D 00 0A 00 ; H.z.............

通过处理批处理文件的cmd.exe将Unicode输出重定向到more,该文件现在以每字符一个字节输出行.但是Windows命令处理器在解释UTF-16 LE编码的行时有一个错误,可以在使用以下命令行时看到它:

The Unicode output is redirected by cmd.exe processing the batch file to more which outputs the lines now with one byte per character. But Windows command processor has a bug on interpreting UTF-16 LE encoded lines as it can be seen on using the following command line:

wmic bios get serialnumber /Format:list | more >output.txt

文件output.txt包含二进制字节:

0000h: 0D 0D 0A 0D 0D 0A 53 65 72 69 61 6C 4E 75 6D 62 ; ......SerialNumb
0010h: 65 72 3D 50 46 31 36 35 48 4E 4E 0D 0D 0A 0D 0D ; er=PF165HNN.....
0020h: 0A 0D 0D 0A 0D 0A 0D 0A                         ; ........

每个Unicode编码的回车+换行(0D 00 0A 00)变为ASCII编码的回车+换行+换行(0D 0D 0A).

Each Unicode encoded carriage return + line-feed (0D 00 0A 00) becomes ASCII encoded carriage return + carriage return + line-feed (0D 0D 0A).

这是真正的问题.使用正则表达式搜索字符串.匹配具有至少一个字符的所有行,以及在从Unicode到ASCII转换不正确的输出上,此正则表达式搜索字符串也匹配空行时,会产生额外的回车结果.

That is the real problem here. The additional carriage return results on using regular expression search string . to match all lines with at least one character that also the empty lines are matched by this regular expression search string on output converted not correct from Unicode to ASCII.

这取决于使用的文本编辑器如何解释无效的换行符序列.大多数文本编辑器将不带换行符的回车符解释为行终止符,但是findstr不会这样做.

It depends on used text editor how the not valid sequence of newline characters are interpreted. Most text editors interpret the carriage return without line-feed as line termination, but findstr does not do that.

一种解决方案是显式搜索包含感兴趣数据的行.

One solution is explicitly searching for the line which contains the data of interest.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
(
    %SystemRoot%\System32\systeminfo.exe | %SystemRoot%\System32\findstr.exe /L /C:"Host Name" /C:"OS Name" /C:"OS Version" /C:"Original Install Date" /C:"System Manufacturer" /C:"System Model" /C:"System Type" /C:"Total Physical Memory"
    %SystemRoot%\System32\wbem\wmic.exe BIOS GET SerialNumber /VALUE | %SystemRoot%\System32\findstr.exe /L /C:SerialNumber
    %SystemRoot%\System32\wbem\wmic.exe CPU GET Name /VALUE | %SystemRoot%\System32\findstr.exe /L /C:Name
    echo %USERDOMAIN%\%USERNAME%
) >"%ComputerName%.txt"
endlocal

写入文件%ComputerName%.txt的数据完全用ASCII编码,到处都只用0D 0A作为行终止符.

The data written into file %ComputerName%.txt is completely encoded in ASCII with everywhere used just 0D 0A as line termination.

有关代码上的小改动的一些其他信息:

Some additional information about small changes on code:

  1. 由于实际上没有必要省略命令more. Windows命令处理器cmd.exe完成了从Unicode到ASCII的不正确转换.
  2. 此批处理文件未启用
  3. 延迟的环境变量扩展,因为根本不需要.
  4. 所有可执行文件均使用众所周知的完整合格文件名指定.因此cmd.exe不必使用环境变量PATHEXTPATH的值来搜索可执行文件.
  5. WMIC 选项/Format:list替换为选项/VALUE,这将导致相同的输出.
  6. FINDSTR 与选项/L一起运行,以明确指示findstr运行文字搜索,尽管这是使用选项/C:的默认选项.
  1. The command more is omitted as not really necessary. The not correct conversion from Unicode to ASCII is done by Windows command processor cmd.exe.
  2. Delayed environment variable expansion is not enabled by this batch file as not needed at all.
  3. All executables are specified with well-known full qualified file name. So cmd.exe has not to search for the executables using the values of the environment variables PATHEXT and PATH.
  4. WMIC option /Format:list is replaced by option /VALUE which results in same output.
  5. FINDSTR is run with option /L to explicitly instruct findstr to run a literal search although that is the default on using option /C:.

更好的批处理文件代码将是:

An even better batch file code would be:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
(
    %SystemRoot%\System32\systeminfo.exe | %SystemRoot%\System32\findstr.exe /L /C:"Host Name" /C:"OS Name" /C:"OS Version" /C:"Original Install Date" /C:"System Manufacturer" /C:"System Model" /C:"System Type" /C:"Total Physical Memory"
    for /F "tokens=1* delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe BIOS GET SerialNumber /VALUE') do if not "%%J" == "" echo Serial Number:             %%J
    for /F "tokens=1* delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe CPU GET Name /VALUE') do if not "%%J" == "" echo CPU Name:                  %%J
    echo Domain\User Name:          %USERDOMAIN%\%USERNAME%
) >"%ComputerName%.txt"
endlocal

WMIC 确定并由 ECHO 输出的其他数据以与systeminfo的输出相同的格式写入文本文件.

The additional data determined with WMIC and output with ECHO are written into the text file in same format as the output of systeminfo.

注意:如果环境变量USERDOMAIN的值或环境变量USERNAME的值包含)&,则最后一个echo命令行不安全. 100%安全是:

Attention: The last echo command line is not safe in case of value of environment variable USERDOMAIN or of environment variable USERNAME contains ) or &. 100% safe would be:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
(
    %SystemRoot%\System32\systeminfo.exe | %SystemRoot%\System32\findstr.exe /L /C:"Host Name" /C:"OS Name" /C:"OS Version" /C:"Original Install Date" /C:"System Manufacturer" /C:"System Model" /C:"System Type" /C:"Total Physical Memory"
    for /F "tokens=1* delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe BIOS GET SerialNumber /VALUE') do if not "%%J" == "" echo Serial Number:             %%J
    for /F "tokens=1* delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe CPU GET Name /VALUE') do if not "%%J" == "" echo CPU Name:                  %%J
) >"%ComputerName%.txt"
setlocal EnableDelayedExpansion
echo Domain\User Name:          !USERDOMAIN!\!USERNAME!>>"%ComputerName%.txt"
endlocal
endlocal

要了解所使用的命令及其工作方式,请打开命令提示符窗口,然后在此处执行以下命令,并非常仔细地阅读每个命令显示的所有帮助页面.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • setlocal /?
  • systeminfo /?
  • wmic /?
  • wmic bios /?
  • wmic bios get /?
  • wmic cpu /?
  • wmic cpu get /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • setlocal /?
  • systeminfo /?
  • wmic /?
  • wmic bios /?
  • wmic bios get /?
  • wmic cpu /?
  • wmic cpu get /?