Batch file 为什么文本文件中的wmic数据会因为一些奇怪的字符编码问题而无法处理?
我一直在编写一个小脚本,确定在我们的特定系统中哪个磁盘可用。磁盘上的空间基本上必须少于1 TB才能被视为可用 这是完整的代码:Batch file 为什么文本文件中的wmic数据会因为一些奇怪的字符编码问题而无法处理?,batch-file,cmd,Batch File,Cmd,我一直在编写一个小脚本,确定在我们的特定系统中哪个磁盘可用。磁盘上的空间基本上必须少于1 TB才能被视为可用 这是完整的代码: @echo off chcp 65001>nul setlocal enabledelayedexpansion wmic logicaldisk get caption,freespace>c:\cmd\1\getDiskInfo.txt for /f "tokens=1,2 eol=C" %%I in (C:\cmd\1\getDiskInfo.t
@echo off
chcp 65001>nul
setlocal enabledelayedexpansion
wmic logicaldisk get caption,freespace>c:\cmd\1\getDiskInfo.txt
for /f "tokens=1,2 eol=C" %%I in (C:\cmd\1\getDiskInfo.txt) do (
set diskCaption=%%I
set diskFreeSpace=%%J
set captionFreeSpace=!diskCaption! !diskFreeSpace!
for /f "tokens=1,2 delims= " %%X in ("!captionFreeSpace!") do (
if [%%Y] neq [] set usedDisks=%%X %%Y
for /f "tokens=1,2 delims= " %%A in ("!usedDisks!") do (
set freeSpaceFirstChar=%%B
set /a freeSpaceFirstChar=!freeSpaceFirstChar:~0,1!
if !freeSpaceFirstChar! gtr 1 set usableDisk=%%A
)
)
)
echo %usableDisk%
pause
但是我得到的%usableDisk%
的输出总是ECHO关闭。
这意味着%usableDisk%
甚至不存在。我做了一些调查,据我所知,这是因为一个字符编码相关的问题。我已经将getDiskInfo.txt
的内容复制到另一个.txt文件中,批处理文件通过我创建的文本文件为我提供了正确的输出。
getDiskInfo.txt
和另一个文本文件的内容都是:
Caption FreeSpace
A:
B: 1098552672256
C: 40824201216
D:
E: 1042498560000
F: 40222941184
原始脚本创建文件的输出为ECHO已关闭。
。我创建的文本文件的输出是F:
,这是正确的输出,因为我们无法使用系统驱动器C:
所以我尝试了echo-END-OF-FILE>>getDiskInfo.txt
,它添加了一行久⁄䙏䘠䱉
添加到脚本创建的文件中,但相同的命令将文件结尾
添加到我的文本文件中。我对这件事完全迷茫了
您是否有任何建议或可能的解决方案?如果我正确理解您的要求,以下简短得多的单行应该只输出可用的驱动器,即不是您的系统驱动器且可用磁盘空间小于1 TB的驱动器
@(对于(“%”中的/F“Tokens=1-2”%%G,逻辑磁盘,其中(DeviceID!=“%SystemDrive%”和FreeSpace^NUL |“%%AppDir%find.exe”“:”)执行@Set“%%H”&SetLocal EnableDelayedExpansion&Echo%%G!\u:~-14!&EndLocal)暂停
正如您在问题中没有解释的那样,您是如何处理检索到的数据的,我只是将它们以与原始
getDiskInfo.txt
类似的格式打印到控制台窗口中(我甚至右对齐了bytes列只是为了好玩!)。此任务需要解决多个问题才能获取磁盘的驱动器号(本地)硬盘驱动器,其可用空间少于一个(1099 511 627 776字节),不是系统驱动器
1.WMIC输出的字符编码
WMIC输出数据时始终使用缩写为UTF-16LE+BOM
所以数据输出
Caption FreeSpace
A:
B: 1098552672256
C: 40824201216
D:
E: 1042498560000
F: 40222941184
作为字节流,字节偏移量左至:
,ASCII表示形式右至;
:
0000h: FF FE 43 00 61 00 70 00 74 00 69 00 6F 00 6E 00 ; ÿþC.a.p.t.i.o.n.
0010h: 20 00 20 00 46 00 72 00 65 00 65 00 53 00 70 00 ; . .F.r.e.e.S.p.
0020h: 61 00 63 00 65 00 20 00 20 00 20 00 20 00 20 00 ; a.c.e. . . . . .
0030h: 20 00 0D 00 0A 00 41 00 3A 00 20 00 20 00 20 00 ; .....A.:. . . .
0040h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ; . . . . . . . .
0050h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ; . . . . . . . .
0060h: 20 00 20 00 20 00 0D 00 0A 00 42 00 3A 00 20 00 ; . . .....B.:. .
0070h: 20 00 20 00 20 00 20 00 20 00 20 00 31 00 30 00 ; . . . . . .1.0.
0080h: 39 00 38 00 35 00 35 00 32 00 36 00 37 00 32 00 ; 9.8.5.5.2.6.7.2.
0090h: 32 00 35 00 36 00 20 00 20 00 0D 00 0A 00 43 00 ; 2.5.6. . .....C.
00a0h: 3A 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ; :. . . . . . . .
00b0h: 34 00 30 00 38 00 32 00 34 00 32 00 30 00 31 00 ; 4.0.8.2.4.2.0.1.
00c0h: 32 00 31 00 36 00 20 00 20 00 20 00 20 00 0D 00 ; 2.1.6. . . . ...
00d0h: 0A 00 44 00 3A 00 20 00 20 00 20 00 20 00 20 00 ; ..D.:. . . . . .
00e0h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ; . . . . . . . .
00f0h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ; . . . . . . . .
0100h: 20 00 0D 00 0A 00 45 00 3A 00 20 00 20 00 20 00 ; .....E.:. . . .
0110h: 20 00 20 00 20 00 20 00 31 00 30 00 34 00 32 00 ; . . . .1.0.4.2.
0120h: 34 00 39 00 38 00 35 00 36 00 30 00 30 00 30 00 ; 4.9.8.5.6.0.0.0.
0130h: 30 00 20 00 20 00 0D 00 0A 00 46 00 3A 00 20 00 ; 0. . .....F.:. .
0140h: 20 00 20 00 20 00 20 00 20 00 20 00 34 00 30 00 ; . . . . . .4.0.
0150h: 32 00 32 00 32 00 39 00 34 00 31 00 31 00 38 00 ; 2.2.2.9.4.1.1.8.
0160h: 34 00 20 00 20 00 20 00 20 00 0D 00 0A 00 ; 4. . . . .....
但是Windows command processor在打开的命令提示符窗口中运行命令chcp
,需要使用as输出对每个字符进行一个字节的编码。代码页取决于为用于运行处理批处理文件的命令进程的帐户配置的国家/地区
更改为Unicode编码的命令行chcp 65001>nul
在这里没有帮助
使用FOR处理UTF-16LE编码的输出会多次直接导致堆栈溢出,请参阅示例
解决方案是将WMIC的输出重定向到一个临时文件中,输出这个临时文件以处理STDOUT(标准输出)在后台使用%ComSpec%/c
启动的命令进程中,使用命令键入,通过执行批处理文件的命令进程捕获此输出,逐行处理此ASCII输出,最后删除临时文件
@echo off
setlocal EnableExtensions DisableDelayedExpansion
%SystemRoot%\System32\wbem\wmic.exe LOGICALDISK GET Caption,FreeSpace >"%TEMP%\%~n0.tmp"
if not exist "%TEMP%\%~n0.tmp" goto EndBatch
for /F "skip=1 tokens=1,2" %%I in ('type "%TEMP%\%~n0.tmp"') do echo %%I %%J
del "%TEMP%\%~n0.tmp"
:EndBatch
endlocal
在这种情况下,FOR处理ASCII字节流:
000h: 43 61 70 74 69 6F 6E 20 20 46 72 65 65 53 70 61 ; Caption FreeSpa
010h: 63 65 20 20 20 20 20 20 0D 0A 41 3A 20 20 20 20 ; ce ..A:
020h: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 ;
030h: 20 20 0D 0A 42 3A 20 20 20 20 20 20 20 31 30 39 ; ..B: 109
040h: 38 35 35 32 36 37 32 32 35 36 20 20 0D 0A 43 3A ; 8552672256 ..C:
050h: 20 20 20 20 20 20 20 34 30 38 32 34 32 30 31 32 ; 408242012
060h: 31 36 20 20 20 20 0D 0A 44 3A 20 20 20 20 20 20 ; 16 ..D:
070h: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 ;
080h: 0D 0A 45 3A 20 20 20 20 20 20 20 31 30 34 32 34 ; ..E: 10424
090h: 39 38 35 36 30 30 30 30 20 20 0D 0A 46 3A 20 20 ; 98560000 ..F:
0a0h: 20 20 20 20 20 34 30 32 32 32 39 34 31 31 38 34 ; 40222941184
0b0h: 20 20 20 20 0D 0A ; ..
但通常最好避免使用临时文件,因为在批处理文件的执行过程中根本不能保证可以创建临时文件
2.系统驱动器并不总是C:
Windows默认安装在驱动器C:上,因此系统驱动器是C:
。但是Windows也可以安装到不同的驱动器上,在这种情况下,系统驱动器不是C:
。任何依赖默认数据而不是使用适当数据的代码都不是好的编写代码
有一个预定义的SystemDrive
,其中包含安装active Windows的驱动器的驱动器号和冒号。环境变量SystemRoot
包含Windows目录的路径,该目录包含目录System32
,列表中的所有可执行文件都不是内部命令s ofcmd.exe
在打开窗口并运行set system
时,可以看到所有这些系统环境变量及其值。仅运行set
会输出所有环境变量及其为当前用户帐户定义的值
3.整数值范围限制为32位有符号整数
Windows命令处理器cmd.exe
在使用set/A
计算算术表达式时,始终仅使用32位有符号整数,并在使用运算符eq
、NEQ
、LSS
、LEQ
、GTR
、GEQ>时,将整数值与命令进行比较代码>
因此整数值范围为−2147483648
到2147483647
。因此最大值为小于2 GiB的一个字节。值为109951162776
需要cmd.exe
不支持的64位整数值范围
顺便说一句:如果[%%Y]neq[]
永远不是一个好的比较,因为[
和]
对于Windows命令处理器没有特殊意义,neq
首先导致将左字符串转换为32位有符号整数值的方法失败,因为[
对于整数值而言是无效字符,因此,如果
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "UsableDrive="
for /F "skip=1 tokens=1,2" %%I in ('""%SystemRoot%\System32\wbem\wmic.exe" LOGICALDISK where (DriveType=3 and FreeSpace^<1099511627776 and DeviceID!='C:') GET DeviceID,FreeSpace 2>nul"') do (
echo Drive %%I has %%J free bytes.
if not defined UsableDrive set "UsableDrive=%%I"
)
if defined UsableDrive echo Selected drive %UsableDrive%
endlocal
C:\Windows\System32\cmd.exe /c ""C:\WINDOWS\System32\wbem\wmic.exe" LOGICALDISK where (DriveType=3 and FreeSpace^<1099511627776 and DeviceID!='C:') GET DeviceID,FreeSpace 2>nul"
"C:\Windows\System32\wbem\wmic.exe" LOGICALDISK where (DriveType=3 and FreeSpace<1099511627776 and DeviceID!='C:') GET DeviceID,FreeSpace