如何在批处理文件中删除换行符?

3

我正在处理批处理文件并将其导出为文本文件。看起来一切都正常,但是当我打开文本文件时,出现了许多新的换行符。因此,我想要删除它们。

@echo OFF
setlocal EnableDelayedExpansion
(
  systeminfo |findstr /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"
    wmic bios get serialnumber /Format:list | more | findstr .
    wmic cpu get name /Format:list | more | findstr .
  echo=%userdomain%\%username%
)> %ComputerName%.txt

结果文本文件没问题,但仍有很多换行符,我想把它们删除。
Host Name:                 PGV-PF165HNN
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.18363 N/A Build 18363
Original Install Date:     7/22/2019, 6:28:01 PM
System Manufacturer:       LENOVO
System Model:              20JM0009US
System Type:               x64-based PC
BIOS Version:              LENOVO N1QET87W (1.62 ), 2/27/2020
Total Physical Memory:     8,072 MB
SerialNumber=PF165HNN

Name=Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz

WINDOM1\brian.lee

首先,您正在使用“ECHO”命令有意地创建新行。 WMIC命令将输出额外的crlf。但是将其导向“MORE”,然后再导向“FINDSTR”的目的是什么? - Squashman
获取更多的结果,只需要清除换行符,我错了,@Squashman? - Headshot
3个回答

3

%SystemRoot%\System32\systeminfo.exe的操作系统语言相关输出以ASCII/ANSI/OEM编码方式字符编码,即每个字符使用一个字节,代码页与在命令提示符窗口中运行时显示的代码页相同。代码页取决于用于运行批处理文件的帐户配置的国家(地区)。只要感兴趣的数据不包含代码值大于127的字符(非ASCII字符),代码页实际上并不重要。

systeminfo通过findstr过滤的输出是二进制的,文件左侧为十六进制偏移量,冒号后为字节的十六进制值,分号后为它们的ASCII表示。

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编码,采用UTF-16小端编码,并带有字节顺序标记(BOM)。因此,两个使用的wmic命令行的输出每个字符占用两个字节。

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

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小端字节顺序标记。每个ASCII字符用两个字节(16位)编码,高字节的值为0。换行符是回车(0D 00)和换行(0A 00)。首先输出两个空行,然后是感兴趣数据所在的行,最后再次输出两个空行。
命令行wmic cpu get name以二进制形式产生输出:
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.............

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

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)。

这是真正的问题。附加的回车导致使用正则表达式搜索字符串.来匹配至少包含一个字符的所有行时,空行也会被此正则表达式搜索字符串匹配,而从Unicode转换为ASCII输出时不正确。

如何解释不合法的换行符序列取决于所使用的文本编辑器。大多数文本编辑器将回车符视为行终止符,但findstr不会这样做。

一种解决方案是显式搜索包含所需数据的行。

@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

关于代码的一些额外信息:

  1. 不再需要使用命令more,因此被省略。将Unicode转换为ASCII采用Windows命令处理器cmd.exe
  2. 该批处理文件未启用延迟环境变量扩展,因为根本不需要。
  3. 所有可执行文件都使用全名指定。因此,cmd.exe无需使用环境变量PATHEXTPATH的值来搜索可执行文件。
  4. WMIC选项/Format:list/VALUE选项替代,输出结果相同。
  5. FINDSTR使用/L选项运行,明确指示findstr进行字面搜索,尽管这在使用/C:选项时是默认的。
一个更好的批处理文件代码是:
@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的输出具有相同格式的文本文件写入。

注意:最后一个echo命令行在环境变量USERDOMAIN或环境变量USERNAME的值包含)&的情况下不安全。100%安全的方法是:

@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

要理解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并完整而仔细地阅读每个命令显示的帮助页面。
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • setlocal /?
  • systeminfo /?
  • wmic /?
  • wmic bios /?
  • wmic bios get /?
  • wmic cpu /?
  • wmic cpu get /?

1

你可以尝试这样做:

创建一个名为sysi.vbs的文件,其中包含以下代码,并以以下方式运行:cscript //nologo sysi.vbs

根据您的需求进行调整。

sysi.vbs: '---------------------------------------------

Set dtmConvertedDate = CreateObject("WbemScripting.SWbemDateTime")

    strComputer = "."
    Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

    Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")

    ' Create a new WshNetwork object to access network properties.
    Wscript.Echo "*** from WScript.Network ***************"

    Set WshNetwork = WScript.CreateObject("WScript.Network")
    Wscript.Echo "Computer name : " & WshNetwork.ComputerName 
    Wscript.Echo "Domain : " & WshNetwork.UserDomain 
    Wscript.Echo "User name : " & WshNetwork.UserName 

    Wscript.Echo "*** from Win32_OperatingSystem  **************"
    For Each objOperatingSystem in colOperatingSystems
        Wscript.Echo "OS Caption: " & objOperatingSystem.Caption
        Wscript.Echo "OS Version: " & objOperatingSystem.Version

        dtmConvertedDate.Value = objOperatingSystem.InstallDate
        dtmInstallDate = dtmConvertedDate.GetVarDate

        Wscript.Echo "OS Install Date: " & dtmInstallDate
        Wscript.Echo "OS Serial Number: " & objOperatingSystem.SerialNumber

    Next

    Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)

    Wscript.Echo "*** from Win32_ComputerSystem **********"

    For Each objItem in colItems
        Wscript.Echo "Manufacturer: " & objItem.Manufacturer
        Wscript.Echo "Model: " & objItem.Model
        Wscript.Echo "SystemType: " & objItem.SystemType
        Wscript.Echo "TotalPhysicalMemory: " & objItem.TotalPhysicalMemory
    Next

    Wscript.Echo "*** from Win32_Processor **********"
    Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor",,48)
    For Each objItem in colItems
        Wscript.Echo "Proc Name: " & objItem.Name
        Wscript.Echo "CurrentClockSpeed: " & objItem.CurrentClockSpeed
        Wscript.Echo "ErrorDescription: " & objItem.ErrorDescription
        Wscript.Echo "DeviceID: " & objItem.DeviceID
        Wscript.Echo "Manufacturer: " & objItem.Manufacturer
        Wscript.Echo "MaxClockSpeed: " & objItem.MaxClockSpeed

    Next



    Wscript.Echo "*** from Win32_BIOS ******* "
    Set colBIOS = objWMIService.ExecQuery("Select * from Win32_BIOS")

    Set dtmRelDateRaw = CreateObject("WbemScripting.SWbemDateTime")

    For each objBIOS in colBIOS
         Wscript.Echo "BIOS Name: " & objBIOS.Name
         Wscript.Echo "BIOS Manufacturer: " & objBIOS.Manufacturer
         Wscript.Echo "Primary BIOS: " & objBIOS.PrimaryBIOS

         dtmRelDateRaw.Value = objBIOS.ReleaseDate
         dtmRelDate = dtmRelDateRaw.GetVarDate
         Wscript.Echo "Release Date: " & objBIOS.ReleaseDate
         Wscript.Echo "Release Date: " & dtmRelDate
         Wscript.Echo "Serial Number: " & objBIOS.SerialNumber
         Wscript.Echo "Status: " & objBIOS.Status
         Wscript.Echo "Version: " & objBIOS.Version


    Next

1

这是一种不使用DisableDelayedExpansion的替代方法:

@echo off && setlocal EnableDelayedExpansion

set "_usrd=Domain\User Name:          !USERDOMAIN!\!USERNAME!" && set "_bios=Bios Serial Number:       -x" && set "_CPUs=CPU Name:                 -y"
set "_wmic=%SystemRoot%\System32\wbem\wmic.exe" && pushd "%SystemRoot%\System32" && >"%temp%\%ComputerName%.txt" 2>nul (
systeminfo.exe | findstr "Host.Name OS.Name OS.Version Original.Install.Date System.Manufacturer System.Model System.Type Total.Physical.Memory"
for /f skip^=1^tokens^=* %%i in ('!_wmic! bios get serialnumber^|findstr "[0-9] [aZ]"')do ^< nul call set /p "'=!_bios:-x= %%~i!" <nul & echo\
for /f skip^=1^tokens^=* %%j in ('!_wmic! cpu get name^|findstr "[0-9] [aZ]"')do ^< nul call set /p "'=!_CPUs:-y= %%~j!" <nul & echo\
echo\!_usrd! ) && type "%temp%\%ComputerName%.txt" && popd && endlocal && goto :EOF 
  • 输出:
Host Name:                 LAME_SLUG
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.18363 N/A Build 18363
Original Install Date:     3/27/2020, 11:17:06 PM
System Manufacturer:       LENOVO
System Model:              80YH
System Type:               x64-based PC
BIOS Version:              LENOVO 4WCN46WW, 12/30/2019
Total Physical Memory:     16,259 MB
Bios Serial Number:        PE03A187
CPU Name:                  Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
Domain\User Name:          LAME_SLUG\ecker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接