这是我在自己的软件更新批处理脚本中使用的子例程:
:getfattr
set %1=
setlocal
set "name=%~f2"
set "name=%name:\=\\%"
for /f "delims=" %%A in ('wmic datafile where "name='%name:'=\'%'" get %1 /format:list') do @^
for /f "delims=" %%B in ("%%A") do endlocal & set "%%B" & goto :eof
echo>&2 getfattr failed
endlocal
goto :eof
它可以获取wmic datafile get支持的任何文件属性。例如,您可以通过以下方式获取当前安装的 Adobe Reader 的文件版本:
call :getfattr version "%ProgramFiles(x86)%\Adobe\Reader 11.0\Reader\AcroRd32.exe"
echo "!version!"
完成后,环境变量version 将包含请求的版本字符串。如果:getfattr 失败,version 保证不会被设置。
该示例的测试执行跟踪如下所示(延迟扩展已启用,尽管 :getfattr 不假定):
>call :getfattr version "C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe"
>set version=
>setlocal
>set "name=C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe"
>set "name=C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe"
>for /F "delims=" %A in ('wmic datafile where "name='C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe'" get version /format:list') do @for /F "delims=" %B in ("%A") do endlocal & set "%B" & goto :eof
>endlocal & set "Version=11.0.18.21" & goto :eof
>echo "!version!"
"11.0.18.21"
正如您所看到的,它非常直接,而且不会太在意。然而,它确实会小心翼翼地穿过cmd 和wmic 的雷区。
首先,您要获取的属性的名称也是您希望结果结束的变量的名称(上面测试中的version)。在子例程中,该名称为%1,因此set %1= 将其清除。
您传入的文件名需要一些预处理才能安全地交给wmic,并且需要一个shell变量,因此发出setlocal以避免踩踏调用者的变量。
set "name=%~f2" 在去掉所有周围的双引号并将其扩展为完整路径名后,将名称复制到环境变量中。双引号将整个 set 参数括起来,以防止路径名中的与号或括号引起的麻烦。
wmic 查询使用类似 SQL 的语法,其中字符串值被单引号 ' 字符包围,\ 是一种转义,可抑制后面字符的任何特殊含义。由于这两个在 Windows 路径名中都是合法的,因此所有出现的任何一个都需要一个 \ 前缀。 set "name=%name:\=\\%" 转义嵌入的反斜杠,wmic 命令行中的 '%name:'=\'%' 构造转义嵌入的单引号并添加所需的周围。
cmd 的解析器不会关闭单引号之间的特殊处理,并且名称周围不再有任何双引号,因此嵌入的空格、括号或 & 符号可能会破坏事物。为了防止这种情况,wmic 的整个 name= 参数被双引号引起来。不需要对名称中已有的双引号进行特殊处理,因为双引号 在 Windows 文件名中被禁止,因此不能有。
包含wmic 命令的for 命令行以@^ 序列结尾。 ^ 用于附加下一行作为外部for 命令的有效负载; @ 防止该有效负载在执行跟踪中回显,即使 ECHO 已打开。
之所以进行回声抑制,主要是因为内部for 的存在只是为了摆脱cmd 将wmic 的输出从Unicode 转换为ASCII 时注入的虚假CR 字符(使用相同的技术)在@dbenham 的回答中),如果允许回显,那些 CR 只会用令人困惑的覆盖弄脏跟踪。附带的好处是,当内部for 从外部for 传递的行包含仅一个CR,其中wmic 的版本依赖编号时,内部for 不会执行其自己的有效负载坚持发射。如果 ECHO 开启,内部 for 的有效负载确实会得到回显,因此跟踪仍会捕获所有有用的事件。
该有效负载由三个 & 分隔的命令组成,for 将在 cmd 处理各个命令之前将其扩展为单个命令行。特别是,这意味着set "%%B" 在endlocal 运行之前被扩展,这会将set 创建的变量放在setlocal/endlocal 范围之外,并使其可供调用者使用。
%%B 将始终以 name=value 格式扩展,因为 /format:list 开关传递给 wmic;该名称将与get 动词指定的名称相同,这就是您传入的名称最终选择您返回的变量的方式。 set 的整个 name=value 参数被引用以防请求的属性包含 shell 特殊字符。这使得 :getfattr 本身是安全的,但您可能想要使用 !delayed!扩展而不是 %premature% 扩展,无论您实际使用它返回给您的变量。
同一行上的& goto :eof 会从两个for 循环中断开,并在内部循环真正执行任何操作后立即返回到:getfattr 的调用者,以防万一你传入一些奇怪的名称并且wmic get 产生更多一个非空行输出。
最后三行只有在wmic 完全没有输出时才会运行,这就是它失败时发生的情况。