从执行批处理文件复制到CSV文件时,WMIC查询输出会有额外的空格

omjgkv6w  于 2023-05-20  发布在  其他
关注(0)|答案(1)|浏览(240)

我正在我工作的几个工厂周围发明一堆电脑。我发现我可以通过创建一个批处理文件来加快这个过程,该批处理文件使用命令提示符执行一个查询列表,并将输出写入CSV文件,然后我可以将其导入Excel电子表格。
这就是批处理文件的样子:

systeminfo | findstr /c:"Host Name" > InventoryData.csv
systeminfo | findstr /c:"Domain" >> InventoryData.csv
systeminfo | findstr /c:"OS Name" >> InventoryData.csv
systeminfo | findstr /c:"OS Version" >> InventoryData.csv
systeminfo | findstr /c:"System Manufacturer" >> InventoryData.csv
systeminfo | findstr /c:"System Model" >> InventoryData.csv
systeminfo | findstr /c:"Total Physical Memory" >> InventoryData.csv

ipconfig | findstr IPv4 >> InventoryData.csv

wmic diskdrive get size >> InventoryData.csv
wmic bios get serialnumber >> InventoryData.csv
wmic cpu get name >> InventoryData.csv

该文件执行完美,并给我一个CSV文件的输出在桌面上。然而,WMIC输出中有一堆额外的无用空格。
原始CSV文件如下所示:

左边是我想要的样子,右边是原始CSV导入到Excel的样子:

我对这一切都很陌生,在网上找不到任何答案。我的主要问题是WMIC输出中的额外空格,但我也希望systeminfo输出排除标签,即我想要LOANERPC7而不是Host Name: LOANERPC7
到目前为止,我的解决方案是手动编辑CSV文件并删除所有多余的空格。我试着在PowerShell中运行一个命令来去除CSV文件中的所有空格,但它并没有消除WMIC输出中的多余空格。

zpjtge22

zpjtge221#

批处理文件代码中存在多个问题。

  1. systeminfo的执行需要相当长的时间。因此,建议只运行一次,并将所有感兴趣的数据写入输出文件。
    1.创建的输出文件肯定不是comma-separated values文件,因此不应具有文件扩展名.csv。它只是一个文本文件,因此文件扩展名应为.txt。另一种可能性是用感兴趣的数据创建真正的CSV文件。
    1.WMIC已被Microsoft声明为已弃用。默认情况下,它不再安装在当前最新的Windows 11上。如果Windows 10/11的22 H2版本安装了以前版本的升级,则它仍然存在于Windows 10/11 22 H2上。
    1.WMIC始终以Unicode格式输出数据,使用UTF-16 Little Endian character encodingbyte order mark(BOM),这意味着每个字符两个字节,通常由WMIC输出文本数据。将WMIC输出的数据直接追加到包含字符的文本文件中,该字符编码仅使用每个字符一个字节,导致文本文件中包含混合编码字符,任何程序都无法正确处理。不需要的“空格”是真实的的空字节,这就是PowerShell命令行无法删除它们的原因。
    1.大多数Windows commands设计用于处理字符的文本,根据为所用帐户配置的国家/地区定义的code page,每个字符仅使用一个字节进行编码。FOR分别为cmd.exe在处理WMIC的UTF-16 LE编码输出时有一个怪癖。它将UTF-16 LE编码的回车和换行字节序列0D 00 0A 00(十六进制)解释为回车+回车+换行,导致在分配给环境变量的数据字符串结尾处出现错误的回车,这会导致字符串值的进一步处理出现问题。
    下面注解的批处理文件考虑了所有这些事实。
@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Delete all environment variables in the local environment beginning with an underscore.
for /F "delims==" %%G in ('set _ 2^>nul') do set "%%G="

rem Run systeminfo.exe, filter the output with findstr.exe, and assign each
rem data of interest to an environment variable of which name ss output by
rem systeminfo.exe left to the colon and with the data string with removal
rem of all spaces/tabs between colon and beginning of data string.
for /F "tokens=1* delims=:" %%G in ('%SystemRoot%\System32\systeminfo.exe 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /L /C:"Host Name" /C:Domain /C:"OS Name" /C:"OS Version" /C:"System Manufacturer" /C:"System Model" /C:"Total Physical Memory"') do for /F tokens^=*^ eol^= %%I in ("%%H") do set "_%%G=%%I"

rem Get the IPv4 addresses of all currently available network adapters. If there
rem is more than one network adapter, separate the IPv4 adresses with a comma
rem and a space which requires enclosing the entire data string in double quotes.
for /F "tokens=1* delims=:" %%G in ('%SystemRoot%\System32\ipconfig.exe 2^>nul ^| %SystemRoot%\System32\findstr.exe /C:"IPv4 Address"') do for /F "tokens=*" %%I in ("%%H") do (
    if not defined _IPv4Addresses (set "_IPv4Addresses=%%I") else for /F "tokens=2 delims==" %%J in ('set _IPv4Addresses') do set "_IPv4Addresses="%%~J, %%I""
)

rem Is wmic.exe not available at all, continue with preparation of the values.
if not exist %SystemRoot%\System32\wbem\wmic.exe goto PrepareValues

rem Run wmic.exe to get the size of all drives. If there is more than one
rem drive, separate the drive sizes with a comma and a space which requires
rem enclosing the entire data string in double quotes.
for /F "tokens=2 delims==" %%G in ('%SystemRoot%\System32\wbem\wmic.exe DISKDRIVE GET Size /VALUE 2^>nul') do for /F "delims=" %%H in ("%%G") do (
    if not defined _DiskDriveSizes (set "_DiskDriveSizes=%%H") else for /F "tokens=2 delims==" %%I in ('set _DiskDriveSizes') do set "_DiskDriveSizes="%%~I, %%H""
)

rem Run wmic.exe to get the BIOS serial number.
for /F "tokens=2 delims==" %%G in ('%SystemRoot%\System32\wbem\wmic.exe BIOS GET SerialNumber /VALUE 2^>nul') do for /F "delims=" %%H in ("%%G") do set "_SerialNumber=%%H"

rem Run wmic.exe to get the name of the CPU.
for /F "tokens=2 delims==" %%G in ('%SystemRoot%\System32\wbem\wmic.exe CPU GET Name /VALUE 2^>nul') do for /F "delims=" %%H in ("%%G") do set "_CPUName=%%H"

:PrepareValues
setlocal EnableDelayedExpansion

rem Enclose the host name value in double quotes on containing a comma.
if not "!_Host Name:,=!" == "!_Host Name!" set "_HostName="!_Host Name!""

rem Enclose the domain value in double quotes on containing a comma.
if not "!_Domain:,=!" == "!_Domain!" set "_Domain="!_Domain!""

rem Enclose the system manufacturer value in double quotes on containing a comma.
if not "!_System Manufacturer:,=!" == "!_System Manufacturer!" set "_System Manufacturer="!_System Manufacturer!""

rem Enclose the system model value in double quotes on containing a comma.
if not "!_System Model:,=!" == "!_System Model!" set "_System Model="!_System Model!""

rem Enclose the CPU name value in double quotes on containing a comma.
if not "!_CPUName:,=!" == "!_CPUName!" set "_CPUName="!_CPUName!""

rem The other values sould never contain a comma or are already enclosed in
rem double quotes on data value contains a comma like on multiple IPv4 addresses.

echo !_Host Name!,!_Domain!,!_OS Name!,!_OS Version!,!_System Manufacturer!,!_System Model!,!_Total Physical Memory!,!_IPv4Addresses!,!_DiskDriveSizes!,!_SerialNumber!,!_CPUName!>"InventoryData_!ComputerName!.csv"
endlocal
endlocal

每个for /F命令都有一个包含在'中的命令行,这会导致在后台启动另一个命令进程,并将%ComSpec% /c和命令行作为附加参数追加,并将所有输出捕获到后台命令进程的STDOUT(标准输出)。例如,命令

for /F "tokens=1* delims=:" %%G in ('%SystemRoot%\System32\systeminfo.exe 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /L /C:"Host Name" /C:Domain /C:"OS Name" /C:"OS Version" /C:"System Manufacturer" /C:"System Model" /C:"Total Physical Memory"') do

导致在后台运行

C:\Windows\System32\cmd.exe /c C:\Windows\System32\systeminfo.exe 2>nul | C:\Windows\System32\findstr.exe /B /L /C:"Host Name" /C:Domain /C:"OS Name" /C:"OS Version" /C:"System Manufacturer" /C:"System Model" /C:"Total Physical Memory"

阅读有关Using command redirection operators的Microsoft文档,了解2>nul|的说明。当Windows命令解释器在执行命令FOR(使用在后台启动的单独命令进程执行嵌入式命令行)之前处理此命令行时,FOR命令行上的重定向运算符>|必须使用插入字符^进行转义,以将其解释为文字字符。
在后台运行wmic.exe的命令行上的内部for /F "delims=" %%H in ("%%G") do用于删 debugging 误的回车。

注1:systeminfo将我的测试尾随空格添加到System Model字符串中,批处理文件不会删除该字符串。
**注2:**双击创建的CSV文件时,Microsoft Excel会将我的计算机中单个安装的硬盘驱动器的磁盘大小的大整数值解释为浮点数。最好使用导入向导导入CSV文件,并确保定义了磁盘大小的数据列格式,以获得显示的真实的字节值。
**注3:**也可以使用%SystemRoot%\System32\reg.exe而不是systeminfo.exewmic.exe直接从Windows注册表中查询感兴趣的数据,这将更快,即使没有安装wmic.exe也可以工作。另一种解决方案是为此数据收集任务编写Windows PowerShell 脚本,而根本不使用Windows * 命令处理器 * 解释的批处理文件。

要了解所使用的命令及其工作方式,请打开command prompt窗口,在那里执行以下命令,并完整仔细地阅读每个命令的帮助页面。

  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • ipconfig /?
  • rem /?
  • set /?
  • setlocal /?
  • systeminfo /?
  • wmic /?
  • wmic bios /?
  • wmic bios get /?
  • wmic cpu /?
  • wmic cpu get /?
  • wmic diskdrive /?
  • wmic diskdrive get /?

还应该阅读Microsoft文档:

相关问题