从批处理文件中查找已安装程序的版本

时间:2014-08-06 13:58:20

标签: batch-file wmic

我们有一个批处理文件,可以在开发人员设置中安装多个程序。当我们获得使用过的组件的新版本时,会定期运行。因此,只有在版本不同的情况下安装才会很好。

在命令提示符下,我可以运行它并返回安装的版本:

wmic datafile where name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe' get version /format:list

其中输出Version=12.1.369.0

然而,当我把它放到像这样的批处理文件中并尝试提取版本时:

echo off
FOR /F "tokens=2 delims==" %%I in ('"wmic datafile where^(name^="C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe" get version /format:list"') DO (SET "RESULT=%%I")
ECHO %RESULT%

我收到回复\\Common was unexpected at this time.

有些部分可能是多余的,因为我一直试图通过网络来解决这个问题。

我错过了什么?

5 个答案:

答案 0 :(得分:8)

你有一套错位的双引号,还有一个额外的(

WMIC使用SQL语法,字符串用单引号括起来。内部单引号不会干扰包含单引号的命令。

您可以在WHERE子句(不包括WHERE关键字)周围加上双引号,以避免FOR DO()子句中的某些转义问题。

@echo off
FOR /F "tokens=2 delims==" %%I IN (
  'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO SET "RESULT=%%I"
ECHO %RESULT%

但这可能不是完整的解决方案。您无法使用上面的代码看到它,但RESULT实际上包含一个尾随回车符(0x0D)。这是由于FOR / F如何处理WMIC unicode输出的怪癖。 WMIC输出的每一行都有额外的尾随回车。

只要您始终使用%RESULT%(正常扩展)访问RESULT,那么您将不会遇到任何问题。但如果您需要延迟扩展,那么您可能会遇到问题,如下所示。

@echo off
setlocal enableDelayedExpansion
FOR /F "tokens=2 delims==" %%I IN (
  'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO SET "RESULT=%%I"
ECHO %RESULT%xxx
ECHO !RESULT!xxx

剥离不需要的回车的一种方便方法是使用额外的FOR级别。

@echo off
setlocal enableDelayedExpansion
FOR /F "tokens=2 delims==" %%I IN (
  'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO FOR /F "delims=" %%A IN ("%%I") DO SET "RESULT=%%A"
ECHO %RESULT%xxx
ECHO !RESULT!xxx

答案 1 :(得分:3)

这是我在自己的软件更新批处理脚本中使用的子程序:

: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"

正如你所看到的,它非常直接,并且不会太过分。但是,它却悄悄穿过cmdwmic陷阱的雷区。

首先,您要获取的属性的名称也是您希望结果在(上面的测试中为version)的变量所使用的名称。在子例程中,该名称为%1,因此set %1=将其清除。

您传入的文件名需要进行一些预处理才能安全地传递给wmic并且需要一个shell变量,因此发出setlocal以避免重写调用者的变量。 / p>

set "name=%~f2"在剥离任何周围的双引号并将其扩展为完整路径名后,将名称复制到环境变量中。双引号括起整个set参数,以防止路径名中的&符号或括号引起的悲伤。

wmic查询使用类似SQL的语法,其中字符串值由单引号'字符包围,\是一个转义符,用于抑制后续字符的任何特殊含义。由于这两者在Windows路径名中都是合法的,因此所有出现的都需要\前缀。 set "name=%name:\=\\%"转义嵌入的反斜杠,'%name:'=\'%'命令行中的wmic构造转义嵌入的单引号,并添加所需的周围引号。

cmd的解析器不会关闭单引号之间的特殊处理,并且名称不再包含任何周围的双引号,因此嵌入的空格,括号或符号可能会破坏事物。为了防范这种情况,wmic的整个name=参数被双重引用。名称中已经不需要对双引号进行特殊处理,因为Windows文件名中禁止使用双引号 ,因此不能有任何。

包含for命令的wmic命令行以@^序列结束。 ^用于附加下一行作为外for命令的有效负载;即使ECHO处于打开状态,@也会阻止有效负载在执行跟踪中回显。

回声抑制主要是因为内部for的存在只是为了摆脱由cmdwmic的输出从Unicode转换为for所引入的虚假CR字符ASCII(与@ dbenham的答案中使用的技术相同)并且如果允许回声,那些CR只会因为混乱的覆盖而污染了跟踪。作为附带好处,当内部for从外部wmic传递的行仅包含 CR时,内部for将不会执行自己的有效内容,这是一个与版本相关的数字其中for坚持要发光。如果启用了ECHO,则内部cmd的有效负载回显,因此跟踪仍会捕获所有有用的事件。

该有效负载由三个& -separated命令组成,set "%%B"将在endlocal处理各个命令之前作为单个命令行扩展。特别是,这意味着setsetlocal运行之前展开,这会将endlocal创建的变量置于%%B / /format:list范围之外并使其成为可能可供来电者使用。

由于wmic切换传递给get

set将始终以 name = value 格式展开;该名称将与& goto :eof动词指定的名称相同,这就是您传递的名称最终选择您获得的变量的方式。如果请求的属性包含shell特殊字符,则引用for的整个 name = value 参数。这使得:getfattr本身安全,但你可能想要使用!延迟!扩展而不是%premature%扩展,只要你实际使用它交给你的变量。

同一行上的wmic get从两个wmic循环中断,并在内部实际执行任何操作后立即返回:getfattr的调用者,以防您传入一些奇怪的名称和{{ 1}}产生多个非空行输出。

如果{{1}}根本没有产生任何输出,那么最后三行只会运行,这就是它失败时会发生的情况。

答案 2 :(得分:0)

这是我的档案文件。

@echo off

If "%~1"=="" goto help
If "%~1"=="/?" goto help
If /i "%~1"=="/h" goto help
If "%~1"=="-?" goto help
If /i "%~1"=="-h" goto help

set filepath=%~f1
set  file=%filepath:\=\\%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version"
echo %errorlevel%
goto finish

:help
Echo.
Echo. FileVer
Echo. -------
Echo.
Echo. Purpose:
Echo.
Echo.    Reports the version number for an exe, dll, and similar files.
Echo.
Echo. Usage:
Echo.
Echo.    filever ^<executable file^>
Echo.
Echo.    filever [/h ^| -h ^| /? ^| -?]    Starts this help
Echo.
echo. Examples:
Echo.
Echo.    filever c:\windows\explorer.exe
Echo.    filever "C:\Program Files\Windows NT\Accessories\wordpad.exe"
Echo.    filever shell32.dll

Echo.
Echo.    For Help
Echo.
Echo.    filever 

Echo.    filever /?
Echo.

:finish
rem Pause if command double clicked
If /i "%cmdcmdline:~0,6%"=="cmd /c" pause

您似乎在整个命令

周围有一组额外的引号

for循环的一个问题是它会在版本之后输出一个空行。

set filepath=%~f1
set  file=%filepath:\=\\%
for /f  "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do set a=%%A & echo %%A

把它放在一个文件中。虽然该文件有两个空行。

set filepath=%~f1
set  file=%filepath:\=\\%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version">test.txt

也许这可以起作用

set filepath=%~f1
set  file=%filepath:\=\\%
for /f  "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do if not ""==%%A set a=%%A & echo %%A

或者拨打另一个批处理文件,不要返回。

这是一种解决空白行的方法。

set filepath=%~f1
set  file=%filepath:\=\\%
for /f  "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"^|findstr /i /v /r "^$"') do set a=%%A & echo %A%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version"|findstr /i /v /r "^$">test.txt

答案 3 :(得分:0)

这是另一种方法,为powershell Get-WmiObject绕过WMIC。

@ECHO OFF
start /b powershell.exe -command "Get-WmiObject -Class CIM_DataFile -Filter \"Name='C:\\Program Files (x86)\\Knuckle.dll'\" | Select-Object Version"
PAUSE

双击.cmd批处理文件时返回此结果。

Version
-------
0.0.0.0

答案 4 :(得分:0)

以及没有外部工具的两种方式 1.的 WMIC

WMIC DATAFILE WHERE name="C:\\install.exe" get Version /format:Textvaluelist

注意文件名的双斜杠。

2的 MAKECAB 因为WMIC没有安装在家庭版本的Windows上,所以makecab将在每台Windows机器上运行:

; @echo off
;;goto :end_help
;;setlocal DsiableDelayedExpansion
;;;
;;;
;;; fileinf /l list of full file paths separated with ;
;;; fileinf /f text file with a list of files to be processed ( one on each line )
;;; fileinf /? prints the help
;;;
;;:end_help

; REM Creating a Newline variable (the two blank lines are required!)
; set NLM=^


; set NL=^^^%NLM%%NLM%^%NLM%%NLM%
; if "%~1" equ "/?" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; if "%~2" equ "" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; setlocal enableDelayedExpansion
; if "%~1" equ "/l" (
;  set "_files=%~2"
;  echo !_files:;=%NL%!>"%TEMP%\file.paths"
;  set _process_file="%TEMP%\file.paths"
;  goto :get_info
; )

; if "%~1" equ "/f" if exist "%~2" (
;  set _process_file="%~2"
;  goto :get_info
; )

; echo incorect parameters & exit /b 1
; :get_info
; set "file_info="

; makecab /d InfFileName=%TEMP%\file.inf /d "DiskDirectory1=%TEMP%" /f "%~f0"  /f %_process_file% /v0>nul

; for /f "usebackq skip=4 delims=" %%f in ("%TEMP%\file.inf") do (
;  set "file_info=%%f"
;  echo !file_info:,=%nl%!
; )

; endlocal
;endlocal
; del /q /f %TEMP%\file.inf 2>nul
; del /q /f %TEMP%\file.path 2>nul
; exit /b 0

.set DoNotCopyFiles=on
.set DestinationDir=;
.set RptFileName=nul
.set InfFooter=;
.set InfHeader=;
.Set ChecksumWidth=8
.Set InfDiskLineFormat=;
.Set Cabinet=off
.Set Compress=off
.Set GenerateInf=ON
.Set InfDiskHeader=;
.Set InfFileHeader=;
.set InfCabinetHeader=;
.Set InfFileLineFormat=",file:*file*,date:*date*,size:*size*,csum:*csum*,time:*time*,vern:*ver*,vers:*vers*,lang:*lang*"

示例输出(它有一个字符串版本,这是对wmic方法的一个小补充:)):

c:> fileinfo.bat /l C:\install.exe
    file:install.exe
    date:11/07/07
    size:562688
    csum:380ef239
    time:07:03:18a
    vern:9.0.21022.8
    vers:9.0.21022.8 built by: RTM
    lang:1033

3 使用 shell.application 和杂交批处理\ jscript.Here tooptipInfo.bat

@if (@X)==(@Y) @end /* JScript comment
    @echo off

    rem :: the first argument is the script name as it will be used for proper help message
    cscript //E:JScript //nologo "%~f0" %*

    exit /b %errorlevel%

@if (@X)==(@Y) @end JScript comment */

////// 
FSOObj = new ActiveXObject("Scripting.FileSystemObject");
var ARGS = WScript.Arguments;
if (ARGS.Length < 1 ) {
 WScript.Echo("No file passed");
 WScript.Quit(1);
}
var filename=ARGS.Item(0);
var objShell=new ActiveXObject("Shell.Application");
/////


//fso
ExistsItem = function (path) {
    return FSOObj.FolderExists(path)||FSOObj.FileExists(path);
}

getFullPath = function (path) {
    return FSOObj.GetAbsolutePathName(path);
}
//

//paths
getParent = function(path){
    var splitted=path.split("\\");
    var result="";
    for (var s=0;s<splitted.length-1;s++){
        if (s==0) {
            result=splitted[s];
        } else {
            result=result+"\\"+splitted[s];
        }
    }
    return result;
}


getName = function(path){
    var splitted=path.split("\\");
    return splitted[splitted.length-1];
}
//

function main(){
    if (!ExistsItem(filename)) {
        WScript.Echo(filename + " does not exist");
        WScript.Quit(2);
    }
    var fullFilename=getFullPath(filename);
    var namespace=getParent(fullFilename);
    var name=getName(fullFilename);
    var objFolder=objShell.NameSpace(namespace);
    var objItem=objFolder.ParseName(name);
    //https://msdn.microsoft.com/en-us/library/windows/desktop/bb787870(v=vs.85).aspx
    WScript.Echo(fullFilename + " : ");
    WScript.Echo(objFolder.GetDetailsOf(objItem,-1));

}

main();

用于cmd.exe:

C:\Windows\System32\cmd.exe :
File description: Windows Command Processor
Company: Microsoft Corporation
File version: 6.3.9600.16384
Date created: ?22-?Aug-?13 ??13:03
Size: 347 KB